/*
 * Copyright (c) 2014. Sensoro Inc.
 * All rights reserved.
 */

package com.sensoro.beacon.kit;

import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.os.Handler;
import android.util.Log;

import com.sensoro.beacon.kit.BaseSettings.SecureBroadcastInterval;

import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * The class of the beacon connection.
 */
public class SensoroBeaconConnection {

    /**
     * Success.
     */
    public static final int SUCCESS = 0;
    /**
     * Failure.
     */
    public static final int FAILURE = 1;
    /**
     * Setting data invalid.
     */
    public static final int SETTING_DATA_INVALID = 2;
    /**
     * Write not permitted.
     */
    public static final int WRITE_NOT_PERMITTED = 3;
    /**
     * Authorization failed.
     */
    public static final int AUTHORIZATION_FAILED = 4;
    /**
     * Connect beacon timeout.
     */
    public static final int CONNECTED_TIME_OUT = 5;
    /**
     * Beacon model error.
     */
    public static final int ERROR_BEACON_MODEL = 6;
    /**
     * The method is not support the current version of Yunzi.
     */
    public static final int NOT_SUPPORT = 7;
    /**
     * Service or characteristic is not existed.
     */
    private static final int SERVICE_OR_CHARACTERISTIC_NOT_EXIST = 8;
    /**
     * Disconnected from remote device.
     */
    private static final int DISCONNECTED_BY_REMOTE_DEVICE = 9;

    private static final int CONNECTE_TIME_OUT = 20 * 1000;

    private static final String WRITE_SECURE_BROADCAST_INTERVAL = "WRITE_SECURE_BROADCAST_INTERVAL";
    private static final String REQUIRE_WRITE_PERMISSION_STRING = "REQUIRE_WRITE_PERMISSION_STRING";
    private static final String REQUIRE_WRITE_PERMISSION_BYTE = "REQUIRE_WRITE_PERMISSION_BYTE";
    private static final String DISABLE_PASSWORD = "DISABLE_PASSWORD";
    private static final String WRITE_PASSWORD_STRING = "WRITE_PASSWORD_STRING";
    private static final String WRITE_PASSWORD_BYTE = "WRITE_PASSWORD_BYTE";
    private static final String DISABLE_IBEACON = "DISABLE_IBEACON";
    private static final String ENABLE_IBEACON = "ENABLE_IBEACON";
    private static final String WRITE_BROADCAST_KEY = "WRITE_BROADCAST_KEY";
    private static final String WRITE_MAJOR_MINOR = "WRITE_MAJOR_MINOR";
    private static final String RELOAD_SENSOR_DATA = "RELOAD_SENSOR_DATA";
    private static final String WRITE_PROXIMITY_UUID = "WRITE_PROXIMITY_UUID";
    private static final String WRITE_SENSOR_SETTINGS = "WRITE_SENSOR_SETTINGS";
    private static final String WRITE_BASE_SETTINGS = "WRITE_BASE_SETTINGS";
    private static final String RESET_TO_FACTORY = "RESET_TO_FACTORY";
    private static final String RESET_ACCELEROMETER_COUNT = "RESET_ACCELEROMETER_COUNT";
    private static final String WRITE_LED = "WRITE_LED";

    private static final String BROADCAST_KEY_SECRET = "password";


    private static final String TAG = SensoroBeaconConnection.class.getSimpleName();

    private Context context;
    private Beacon beacon;
    private BeaconConnectionCallback callback;

    private BluetoothLEHelper bluetoothLEHelper;
    private int listenType = 0; // 0.表示写温度的描述. 1.表示写光强的描述. 2.表示写加速度传感器移动次数. 3.表示加速度传感器是否移动

    private BaseSettings baseSettings;
    private SensorSettings sensorSettings;
    private BaseSettings writeBaseSettings;
    private SensorSettings writeSensorSettings;
    private boolean haveWritePassword = false;
    private boolean isDisabledPassword;
    private String uuid; // 写入成功更新 beacon
    private int major; // 写入成功更新 beacon
    private int minor; // 写入成功更新 beacon
    private String beaconType; // beacon 型号
    private String beaconFirmware; // beacon 固件版本
    private SecureBroadcastInterval secureBroadcastInterval; // 写入成功更新 beacon
    private boolean isIBeaconEnabled = true;

    private boolean isConnected;
    private Handler handler;
    private TimeOutRunnable timeOutRunnable;
    private ConnectTimeOutRunnable connectTimeOutRunnable;

    // 写入和校验密码秘钥
    private static final String PASSWORD_KEY = "CtKnQ8BVb3C2khd6HQv6FFBuoHzxWi";
    private boolean isKeyZero;

    private HashMap<String, WriteCallback> writeCallbackHashMap;
    private ConnectCallback connectCallback;

    public SensoroBeaconConnection(Context context, Beacon beacon, BeaconConnectionCallback callback) throws SensoroException {
        if (context == null) {
            throw new SensoroException("Context is null");
        }
        if (beacon == null) {
            throw new SensoroException("Beacon is null");
        }
        if (callback == null) {
            throw new SensoroException("BeaconConnectionCallback is null");
        }
        this.callback = callback;
        this.context = context;
        this.beacon = beacon;

        // 初始化 ble 操作对象
        bluetoothLEHelper = new BluetoothLEHelper(context);

        baseSettings = new BaseSettings();
        sensorSettings = new SensorSettings();

        handler = new Handler();
        timeOutRunnable = new TimeOutRunnable();
        connectTimeOutRunnable = new ConnectTimeOutRunnable();

        beaconType = beacon.getHardwareModelName();
        beaconFirmware = beacon.getFirmwareVersion();
        writeCallbackHashMap = new HashMap<>();
        connectCallback = new ConnectCallback() {
            @Override
            public void onConnectedState(int newState, int status) {

            }
        };
    }

    private SensoroBeaconConnection(Context context, Beacon beacon) {
        writeCallbackHashMap = new HashMap<>();

        this.context = context;
        this.beacon = beacon;

        baseSettings = new BaseSettings();
        sensorSettings = new SensorSettings();

        handler = new Handler();
        timeOutRunnable = new TimeOutRunnable();
        connectTimeOutRunnable = new ConnectTimeOutRunnable();

        beaconType = beacon.getHardwareModelName();
        beaconFirmware = beacon.getFirmwareVersion();

        callback = new BeaconConnectionCallback() {
            @Override
            public void onConnectedState(Beacon beacon, int newState, int status) {

            }

            @Override
            public void onWritePassword(Beacon beacon, int status) {

            }

            @Override
            public void onDisablePassword(Beacon beacon, int status) {

            }

            @Override
            public void onRequireWritePermission(Beacon beacon, int status) {

            }

            @Override
            public void onWriteBaseSetting(Beacon beacon, int status) {

            }

            @Override
            public void onWirteSensorSetting(Beacon beacon, int status) {

            }

            @Override
            public void onWriteMajorMinor(Beacon beacon, int status) {

            }

            @Override
            public void onWriteProximityUUID(Beacon beacon, int status) {

            }

            @Override
            public void onWriteSecureBroadcastInterval(Beacon beacon, int status) {

            }

            @Override
            public void onEnableIBeacon(Beacon beacon, int status) {

            }

            @Override
            public void onDisableIBeacon(Beacon beacon, int status) {

            }

            @Override
            public void onWriteBroadcastKey(Beacon beacon, int status) {

            }

            @Override
            public void onResetToFactorySettings(Beacon beacon, int status) {

            }

            @Override
            public void onResetAcceleratorCount(Beacon beacon, int status) {

            }

            @Override
            public void onReloadSensorData(Beacon beacon, int status) {

            }

            @Override
            public void onUpdateTemperatureData(Beacon beacon, Integer temperature) {

            }

            @Override
            public void onUpdateLightData(Beacon beacon, Double brightnessLux) {

            }

            @Override
            public void onUpdateMovingState(Beacon beacon, Beacon.MovingState movingState) {

            }

            @Override
            public void onUpdateAccelerometerCount(Beacon beacon, int count) {

            }

            @Override
            public void onFlashLightWitCommand(Beacon beacon, int status) {

            }
        };
    }

    /**
     * Connect the beacon.The callback
     * {@link com.sensoro.beacon.kit.SensoroBeaconConnection.BeaconConnectionCallback#onConnectedState(Beacon, int, int)}
     * will be called.
     */
    public void connect() {
        // beacon 型号不是 A0,B0,C0,回调错误
        if (!isA0Beacon() && !isB0Beacon() && !isC0Beacon()) {
            callback.onConnectedState(beacon, Beacon.CONNECTED, ERROR_BEACON_MODEL);
            return;
        }
        if (!bluetoothLEHelper.initialize()) {
            callback.onConnectedState(beacon, Beacon.CONNECTED, FAILURE);
            if (SensoroBeaconManager.DEBUG) {
                Log.d(TAG, "connect---callback connect failure:bleHelper init failure");
            }
            return;
        }
        if (!bluetoothLEHelper.connect(beacon.getMacAddress(), bluetoothGattCallback)) {
            callback.onConnectedState(beacon, Beacon.CONNECTED, FAILURE);
            if (SensoroBeaconManager.DEBUG) {
                Log.d(TAG, "connect---callback connect failure:bleHelper connect failure");
            }
        }
        // timeOut 时间之后判断是否超时
        handler.postDelayed(timeOutRunnable, CONNECTE_TIME_OUT);
        if (SensoroBeaconManager.DEBUG) {
            Log.d(TAG, "connect timeout---start time out runnable");
        }
    }

    private void connect(ConnectCallback connectCallback) {
        this.connectCallback = connectCallback;
        // beacon 型号不是 A0,B0,C0,回调错误
        if (!isA0Beacon() && !isB0Beacon() && !isC0Beacon()) {
            connectCallback.onConnectedState(ConnectCallback.STATE_CONNECTED, ConnectCallback.ERROR_BEACON_MODEL);
            return;
        }
        if (!bluetoothLEHelper.initialize()) {
            connectCallback.onConnectedState(ConnectCallback.STATE_CONNECTED, ConnectCallback.FAILURE);
            if (SensoroBeaconManager.DEBUG) {
                Log.d(TAG, "connect---callback connect failure:bleHelper init failure");
            }
            return;
        }
        if (!bluetoothLEHelper.connect(beacon.getMacAddress(), bluetoothGattCallback)) {
            connectCallback.onConnectedState(ConnectCallback.STATE_CONNECTED, ConnectCallback.FAILURE);
            if (SensoroBeaconManager.DEBUG) {
                Log.d(TAG, "connect---callback connect failure:bleHelper connect failure");
            }
        }
        // timeOut 时间之后判断是否超时
        handler.postDelayed(connectTimeOutRunnable, CONNECTE_TIME_OUT);
        if (SensoroBeaconManager.DEBUG) {
            Log.d(TAG, "connect timeout---start time out runnable");
        }
    }

    /**
     * Connect the beacon with timeout.
     *
     * @param timeOut timeout.If connecting beacon over time, then the callback
     *                {@link com.sensoro.beacon.kit.SensoroBeaconConnection.BeaconConnectionCallback#onConnectedState(Beacon, int, int)}
     *                will be called, and the state is
     *                {@link com.sensoro.beacon.kit.SensoroBeaconConnection#CONNECTED_TIME_OUT}
     *                .
     */
    private void connect(long timeOut) {
        if (!bluetoothLEHelper.initialize()) {
            callback.onConnectedState(beacon, Beacon.CONNECTED, FAILURE);
            if (SensoroBeaconManager.DEBUG) {
                Log.d(TAG, "connect timeout---callback connect failure:bleHelper init failure");
            }
            return;
        }
        if (!bluetoothLEHelper.connect(beacon.getMacAddress(), bluetoothGattCallback)) {
            callback.onConnectedState(beacon, Beacon.CONNECTED, FAILURE);
            if (SensoroBeaconManager.DEBUG) {
                Log.d(TAG, "connect timeout---callback connect failure:bleHelper connect failure");
            }
        }
        // timeOut 时间之后判断是否超时
        handler.postDelayed(timeOutRunnable, timeOut);
        if (SensoroBeaconManager.DEBUG) {
            Log.d(TAG, "connect timeout---start time out runnable");
        }
    }

    class TimeOutRunnable implements Runnable {
        @Override
        public void run() {
            if (!isConnected) {
                close();
                callback.onConnectedState(beacon, Beacon.CONNECTED, CONNECTED_TIME_OUT);
                if (SensoroBeaconManager.DEBUG) {
                    Log.d(TAG, "TimeOutRunnable---callback connect failure:connect time out");
                }
            }
        }
    }

    class ConnectTimeOutRunnable implements Runnable {
        @Override
        public void run() {
            if (!isConnected) {
                close();
                connectCallback.onConnectedState(ConnectCallback.STATE_CONNECTED, ConnectCallback.CONNECTED_TIME_OUT);
                if (SensoroBeaconManager.DEBUG) {
                    Log.d(TAG, "ConnectTimeOutRunnable---callback connect failure:connect time out");
                }
            }
        }
    }

    /**
     * Disconnect the beacon.
     */
    public void disconnect() {
        // if (bluetoothLEHelper != null) {
        // bluetoothLEHelper.disconnect();
        // } else {
        // callback.onConnectedState(beacon, Beacon.DISCONNECTED, FAILURE);
        // if(SensoroBeaconManager.DEBUG){
        // Log.d(TAG,"disconnect---callback disconnect failure:bleHelper is null");
        // }
        // }
        if (close()) {
            callback.onConnectedState(beacon, Beacon.DISCONNECTED, SUCCESS);
            connectCallback.onConnectedState(ConnectCallback.STATE_DISCONNECTED, ConnectCallback.SUCCESS);
            isConnected = false;
        } else {
            callback.onConnectedState(beacon, Beacon.DISCONNECTED, FAILURE);
            connectCallback.onConnectedState(ConnectCallback.STATE_DISCONNECTED, ConnectCallback.FAILURE);
        }
    }

    /**
     * Close the connection of the beacon.
     */
    private boolean close() {
        return bluetoothLEHelper.close();
    }

    /**
     * Return the status whether the beacon is connected or not.
     */
    public boolean isConnected() {
        return isConnected;
    }

    /**
     * Reset to factory.
     */
    public void resetToFactorySettings() {
        // 写入前判断是否加密
        if (beacon.isPasswordEnabled && !haveWritePassword) {
            callback.onResetToFactorySettings(beacon, WRITE_NOT_PERMITTED);
            return;
        }
        if (!bluetoothLEHelper.resetToFactorySettings()) {
            callback.onResetToFactorySettings(beacon, FAILURE);
        }
    }

    /**
     * Reset to factory.
     */
    private void resetToFactorySettings(WriteCallback writeCallback) {
        writeCallbackHashMap.put(RESET_TO_FACTORY, writeCallback);
        // 写入前判断是否加密
        if (beacon.isPasswordEnabled && !haveWritePassword) {
            writeCallback.onFailure(WRITE_NOT_PERMITTED);
            return;
        }
        if (!bluetoothLEHelper.resetToFactorySettings()) {
            writeCallback.onFailure(SERVICE_OR_CHARACTERISTIC_NOT_EXIST);
        }
    }

    /**
     * Reset the count of the acceleration sensor.
     */
    public void resetAccelerometerCount() {
        // 写入前判断是否加密
        if (beacon.isPasswordEnabled && !haveWritePassword) {
            callback.onResetAcceleratorCount(beacon, WRITE_NOT_PERMITTED);
            return;
        }
        if (!bluetoothLEHelper.resetAccelerometerCount()) {
            callback.onResetAcceleratorCount(beacon, FAILURE);
        }
    }

    /**
     * Reset the count of the acceleration sensor.
     */
    private void resetAccelerometerCount(WriteCallback writeCallback) {
        writeCallbackHashMap.put(RESET_ACCELEROMETER_COUNT, writeCallback);
        // 写入前判断是否加密
        if (beacon.isPasswordEnabled && !haveWritePassword) {
            writeCallback.onFailure(WRITE_NOT_PERMITTED);
            return;
        }
        if (!bluetoothLEHelper.resetAccelerometerCount()) {
            writeCallback.onFailure(SERVICE_OR_CHARACTERISTIC_NOT_EXIST);
        }
    }

    /**
     * Set a new password of the beacon.If you set the new password
     * successfully,you must check the password one time before other operations
     * after connecting successfully.
     *
     * @param password new password.
     */
    public void writePassword(String password) {
        // 写入前判断是否加密
        if (beacon.isPasswordEnabled && !haveWritePassword) {
            callback.onWritePassword(beacon, WRITE_NOT_PERMITTED);
            return;
        }

        if (password == null) {
            callback.onWritePassword(beacon, SETTING_DATA_INVALID);
            return;
        }

        byte[] newPassword = new byte[16];
        // 传进密码经过 HMAC-SHA512 算法加密，取前 16 字节
        byte[] passwordBytes = null;
        try {
            passwordBytes = password.getBytes("UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            callback.onWritePassword(beacon, SETTING_DATA_INVALID);
            return;
        }
        byte[] inPassword = SensoroUtils.HMacSHA512(passwordBytes, PASSWORD_KEY);
        // 复制新密码
        System.arraycopy(inPassword, 0, newPassword, 0, 16);

        if (!bluetoothLEHelper.updateWritePassword(newPassword)) {
            callback.onWritePassword(beacon, FAILURE);
        }

        isDisabledPassword = false; // 表示关闭密码操作
    }

    /**
     * Set a new password of the beacon.If you set the new password
     * successfully,you must check the password one time before other operations
     * after connecting successfully.
     *
     * @param password new password.
     */
    private void writePassword(String password, WriteCallback writeCallback) {
        writeCallbackHashMap.put(WRITE_PASSWORD_STRING, writeCallback);
        // 写入前判断是否加密
        if (beacon.isPasswordEnabled && !haveWritePassword) {
            writeCallback.onFailure(WRITE_NOT_PERMITTED);
            return;
        }

        if (password == null) {
            writeCallback.onFailure(SETTING_DATA_INVALID);
            return;
        }

        byte[] newPassword = new byte[16];
        // 传进密码经过 HMAC-SHA512 算法加密，取前 16 字节
        byte[] passwordBytes = null;
        try {
            passwordBytes = password.getBytes("UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            writeCallback.onFailure(SETTING_DATA_INVALID);
            return;
        }
        byte[] inPassword = SensoroUtils.HMacSHA512(passwordBytes, PASSWORD_KEY);
        // 复制新密码
        System.arraycopy(inPassword, 0, newPassword, 0, 16);

        if (!bluetoothLEHelper.updateWritePassword(newPassword)) {
            writeCallback.onFailure(SERVICE_OR_CHARACTERISTIC_NOT_EXIST);
        }

        isDisabledPassword = false; // 表示关闭密码操作
    }

    /**
     * Set a new password of the beacon with bytes.If you set the new password
     * successfully,you must check the password one time before other operations
     * after connecting successfully.
     *
     * @param password new password.
     */
    public void writePassword(byte[] password) {
        // 写入前判断是否加密
        if (beacon.isPasswordEnabled && !haveWritePassword) {
            callback.onWritePassword(beacon, WRITE_NOT_PERMITTED);
            return;
        }

        if (password == null) {
            callback.onWritePassword(beacon, SETTING_DATA_INVALID);
            return;
        }

        byte[] newPassword = new byte[16];
        // 传进密码经过 HMAC-SHA512 算法加密，取前 16 字节
        byte[] inPassword = SensoroUtils.HMacSHA512(password, PASSWORD_KEY);
        // 复制新密码
        System.arraycopy(inPassword, 0, newPassword, 0, 16);

        if (!bluetoothLEHelper.updateWritePassword(newPassword)) {
            callback.onWritePassword(beacon, FAILURE);
        }

        isDisabledPassword = false; // 表示关闭密码操作
    }

    /**
     * Set a new password of the beacon with bytes.If you set the new password
     * successfully,you must check the password one time before other operations
     * after connecting successfully.
     *
     * @param password new password.
     */
    private void writePassword(byte[] password, WriteCallback writeCallback) {
        writeCallbackHashMap.put(WRITE_PASSWORD_BYTE, writeCallback);
        // 写入前判断是否加密
        if (beacon.isPasswordEnabled && !haveWritePassword) {
            writeCallback.onFailure(WRITE_NOT_PERMITTED);
            return;
        }

        if (password == null) {
            writeCallback.onFailure(SETTING_DATA_INVALID);
            return;
        }

        byte[] newPassword = new byte[16];
        // 传进密码经过 HMAC-SHA512 算法加密，取前 16 字节
        byte[] inPassword = SensoroUtils.HMacSHA512(password, PASSWORD_KEY);
        // 复制新密码
        System.arraycopy(inPassword, 0, newPassword, 0, 16);

        if (!bluetoothLEHelper.updateWritePassword(newPassword)) {
            writeCallback.onFailure(SERVICE_OR_CHARACTERISTIC_NOT_EXIST);
        }

        isDisabledPassword = false; // 表示关闭密码操作
    }

    /**
     * Disabled the password of the beacon.
     */
    public void disablePassword() {
        // 写入前判断是否加密
        if (beacon.isPasswordEnabled && !haveWritePassword) {
            callback.onDisablePassword(beacon, WRITE_NOT_PERMITTED);
            return;
        }

        isDisabledPassword = true; // 表示关闭密码操作
        byte[] disabledPassword = new byte[16]; // 写入全 0 表示关闭密码
        if (!bluetoothLEHelper.updateWritePassword(disabledPassword)) {
            isDisabledPassword = false;
            callback.onDisablePassword(beacon, FAILURE);
        }
    }

    /**
     * Disabled the password of the beacon.
     */
    private void disablePassword(WriteCallback writeCallback) {
        writeCallbackHashMap.put(DISABLE_PASSWORD, writeCallback);
        // 写入前判断是否加密
        if (beacon.isPasswordEnabled && !haveWritePassword) {
            writeCallback.onFailure(WRITE_NOT_PERMITTED);
            return;
        }

        isDisabledPassword = true; // 表示关闭密码操作
        byte[] disabledPassword = new byte[16]; // 写入全 0 表示关闭密码
        if (!bluetoothLEHelper.updateWritePassword(disabledPassword)) {
            isDisabledPassword = false;
            writeCallback.onFailure(SERVICE_OR_CHARACTERISTIC_NOT_EXIST);
        }
    }

    /**
     * Check the password.If the beacon has the password,you must check the
     * password one time at first before other operations.
     *
     * @param password the password to check.
     */
    public void requireWritePermission(String password) {
        // 传进密码经过 HMAC-SHA512 算法加密，取前 6 字节
        byte[] newPassword = new byte[16];
        byte[] passwordBytes = null;
        try {
            passwordBytes = password.getBytes("UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            callback.onRequireWritePermission(beacon, FAILURE);
            return;
        }
        byte[] inPassword = SensoroUtils.HMacSHA512(passwordBytes, PASSWORD_KEY);

        // 复制新秘钥
        System.arraycopy(inPassword, 0, newPassword, 0, 16);
        if (!bluetoothLEHelper.requireWritePermission(newPassword)) {
            callback.onRequireWritePermission(beacon, FAILURE);
        }
    }

    /**
     * Check the password.If the beacon has the password,you must check the
     * password one time at first before other operations.
     *
     * @param password the password to check.
     */
    private void requireWritePermission(String password, WriteCallback writeCallback) {
        writeCallbackHashMap.put(REQUIRE_WRITE_PERMISSION_STRING, writeCallback);
        // 传进密码经过 HMAC-SHA512 算法加密，取前 6 字节
        byte[] newPassword = new byte[16];
        byte[] passwordBytes = null;
        try {
            passwordBytes = password.getBytes("UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            writeCallback.onFailure(SETTING_DATA_INVALID);
            return;
        }
        byte[] inPassword = SensoroUtils.HMacSHA512(passwordBytes, PASSWORD_KEY);

        // 复制新秘钥
        System.arraycopy(inPassword, 0, newPassword, 0, 16);
        if (!bluetoothLEHelper.requireWritePermission(newPassword)) {
            writeCallback.onFailure(SERVICE_OR_CHARACTERISTIC_NOT_EXIST);
        }
    }


    /**
     * Check the password with bytes.If the beacon has the password,you must check the
     * password one time at first before other operations.
     *
     * @param password the password to check.
     */
    public void requireWritePermission(byte[] password) {
        // 传进密码经过 HMAC-SHA512 算法加密，取前 6 字节
        byte[] newPassword = new byte[16];
        byte[] inPassword = SensoroUtils.HMacSHA512(password, PASSWORD_KEY);

        // 复制新秘钥
        System.arraycopy(inPassword, 0, newPassword, 0, 16);
        if (!bluetoothLEHelper.requireWritePermission(newPassword)) {
            callback.onRequireWritePermission(beacon, FAILURE);
        }
    }

    /**
     * Check the password with bytes.If the beacon has the password,you must check the
     * password one time at first before other operations.
     *
     * @param password the password to check.
     */
    private void requireWritePermission(byte[] password, WriteCallback writeCallback) {
        writeCallbackHashMap.put(REQUIRE_WRITE_PERMISSION_BYTE, writeCallback);
        // 传进密码经过 HMAC-SHA512 算法加密，取前 6 字节
        byte[] newPassword = new byte[16];
        byte[] inPassword = SensoroUtils.HMacSHA512(password, PASSWORD_KEY);

        // 复制新秘钥
        System.arraycopy(inPassword, 0, newPassword, 0, 16);
        if (!bluetoothLEHelper.requireWritePermission(newPassword)) {
            writeCallback.onFailure(SERVICE_OR_CHARACTERISTIC_NOT_EXIST);
        }
    }

    /**
     * Modify the base settings of the beacon.
     *
     * @param baseSetttings The base settings to set.
     */
    public void writeBaseSettings(BaseSettings baseSetttings) {
        // 写入前判断是否加密
        if (beacon.isPasswordEnabled && !haveWritePassword) {
            callback.onWriteBaseSetting(beacon, WRITE_NOT_PERMITTED);
            return;
        }

        // A0 和 B0,C0 写入 baseSetting 长度不同
        byte[] newBaseSettings = null;
        if (isA0Beacon()) {
            newBaseSettings = new byte[6];
        } else if (isB0Beacon() || isC0Beacon()) {
            newBaseSettings = new byte[4];
        } else {
            callback.onWriteBaseSetting(beacon, ERROR_BEACON_MODEL);
            return;
        }

        // 校验 BaseSetting 是否合法
        if (!checkBaseSettingsLegal(baseSetttings)) {
            callback.onWriteBaseSetting(beacon, SETTING_DATA_INVALID);
            return;
        } else {
            // 数据合法,转换为字节数组
            convertBaseSettingsToBytes(baseSetttings, newBaseSettings);
        }
        if (!bluetoothLEHelper.writeBaseSettings(newBaseSettings)) {
            callback.onWriteBaseSetting(beacon, FAILURE);
        }
    }

    /**
     * Modify the base settings of the beacon.
     *
     * @param baseSetttings The base settings to set.
     */
    private void writeBaseSettings(BaseSettings baseSetttings, WriteCallback writeCallback) {
        writeCallbackHashMap.put(WRITE_BASE_SETTINGS, writeCallback);
        // 写入前判断是否加密
        if (beacon.isPasswordEnabled && !haveWritePassword) {
            writeCallback.onFailure(WRITE_NOT_PERMITTED);
            return;
        }

        // A0 和 B0,C0 写入 baseSetting 长度不同
        byte[] newBaseSettings = null;
        if (isA0Beacon()) {
            newBaseSettings = new byte[6];
        } else if (isB0Beacon() || isC0Beacon()) {
            newBaseSettings = new byte[4];
        } else {
            writeCallback.onFailure(ERROR_BEACON_MODEL);
            return;
        }

        // 校验 BaseSetting 是否合法
        if (!checkBaseSettingsLegal(baseSetttings)) {
            writeCallback.onFailure(SETTING_DATA_INVALID);
            return;
        } else {
            // 数据合法,转换为字节数组
            convertBaseSettingsToBytes(baseSetttings, newBaseSettings);
        }
        if (!bluetoothLEHelper.writeBaseSettings(newBaseSettings)) {
            writeCallback.onFailure(SERVICE_OR_CHARACTERISTIC_NOT_EXIST);
        }
    }

    /**
     * Modify the sensor settings of the beacon.
     *
     * @param sensorSettings The sensor settings to set.
     */
    public void writeSensorSettings(SensorSettings sensorSettings) {
        // 写入前判断是否加密
        if (beacon.isPasswordEnabled && !haveWritePassword) {
            callback.onWirteSensorSetting(beacon, WRITE_NOT_PERMITTED);
            return;
        }
        byte[] newSensoSettings = new byte[6];
        // 校验 SensoSettings 是否合法
        if (!checkSensoSettingsLegal(sensorSettings)) {
            callback.onWirteSensorSetting(beacon, SETTING_DATA_INVALID);
            return;
        } else {
            // 数据合法,转换为字节数组
            convertSensoSettingsToBytes(sensorSettings, newSensoSettings);
        }

        if (!bluetoothLEHelper.writeSensoSettings(newSensoSettings)) {
            callback.onWirteSensorSetting(beacon, FAILURE);
        }
    }

    /**
     * Modify the sensor settings of the beacon.
     *
     * @param sensorSettings The sensor settings to set.
     */
    private void writeSensorSettings(SensorSettings sensorSettings, WriteCallback writeCallback) {
        writeCallbackHashMap.put(WRITE_SENSOR_SETTINGS, writeCallback);
        // 写入前判断是否加密
        if (beacon.isPasswordEnabled && !haveWritePassword) {
            writeCallback.onFailure(WRITE_NOT_PERMITTED);
            return;
        }
        byte[] newSensoSettings = new byte[6];
        // 校验 SensoSettings 是否合法
        if (!checkSensoSettingsLegal(sensorSettings)) {
            writeCallback.onFailure(SETTING_DATA_INVALID);
            return;
        } else {
            // 数据合法,转换为字节数组
            convertSensoSettingsToBytes(sensorSettings, newSensoSettings);
        }

        if (!bluetoothLEHelper.writeSensoSettings(newSensoSettings)) {
            writeCallback.onFailure(SERVICE_OR_CHARACTERISTIC_NOT_EXIST);
        }
    }

    /**
     * Set the UUID of the beacon.
     *
     * @param proximityUuid The UUID to set.
     *                      <p>
     *                      (format:"00000000-0000-0000-0000-000000000000")
     *                      </p>
     */
    public void writeProximityUUID(String proximityUuid) {
        uuid = proximityUuid;
        // 写入前判断是否加密
        if (beacon.isPasswordEnabled && !haveWritePassword) {
            callback.onWriteProximityUUID(beacon, WRITE_NOT_PERMITTED);
            return;
        }
        byte[] uuid = new byte[16];
        // 校验 UUID 是否合法
        if (!checkUUIDLegal(proximityUuid)) {
            callback.onWriteProximityUUID(beacon, SETTING_DATA_INVALID);
            return;
        } else {
            // 数据合法,转换为字节数组过程中判断是否包含非 0~F 的非法字符否则转换失败
            if (!convertUUIDToBytes(proximityUuid, uuid)) {
                callback.onWriteProximityUUID(beacon, SETTING_DATA_INVALID);
                return;
            }
        }
        if (!bluetoothLEHelper.writeProximityUUID(uuid)) {
            callback.onWriteProximityUUID(beacon, FAILURE);
        }
    }

    /**
     * Set the UUID of the beacon.
     *
     * @param proximityUuid The UUID to set.
     *                      <p>
     *                      (format:"00000000-0000-0000-0000-000000000000")
     *                      </p>
     */
    private void writeProximityUUID(String proximityUuid, WriteCallback writeCallback) {
        writeCallbackHashMap.put(WRITE_PROXIMITY_UUID, writeCallback);
        uuid = proximityUuid;
        // 写入前判断是否加密
        if (beacon.isPasswordEnabled && !haveWritePassword) {
            writeCallback.onFailure(WRITE_NOT_PERMITTED);
            return;
        }
        byte[] uuid = new byte[16];
        // 校验 UUID 是否合法
        if (!checkUUIDLegal(proximityUuid)) {
            writeCallback.onFailure(SETTING_DATA_INVALID);
            return;
        } else {
            // 数据合法,转换为字节数组过程中判断是否包含非 0~F 的非法字符否则转换失败
            if (!convertUUIDToBytes(proximityUuid, uuid)) {
                writeCallback.onFailure(SETTING_DATA_INVALID);
                return;
            }
        }
        if (!bluetoothLEHelper.writeProximityUUID(uuid)) {
            writeCallback.onFailure(SERVICE_OR_CHARACTERISTIC_NOT_EXIST);
        }
    }

    /**
     * Set the major and minor of the beacon.
     *
     * @param major (range:0~65535)
     * @param minor (range:0~65535)
     */
    public void writeMajorMinor(int major, int minor) {
        this.major = major;
        this.minor = minor;
        // 写入前判断是否加密
        if (beacon.isPasswordEnabled && !haveWritePassword) {
            callback.onWriteMajorMinor(beacon, WRITE_NOT_PERMITTED);
            return;
        }
        byte[] majorMinor = new byte[4];
        // 校验 major 和 minor 是否合法
        if (!checkMajorMinorLegal(major, minor)) {
            callback.onWriteMajorMinor(beacon, SETTING_DATA_INVALID);
            return;
        } else {
            // 数据合法,转换为字节数组
            convertMajorMinorToBytes(major, minor, majorMinor);
        }
        if (!bluetoothLEHelper.writeMajorMinor(majorMinor)) {
            callback.onWriteMajorMinor(beacon, FAILURE);
        }
    }

    /**
     * Set the major and minor of the beacon.
     *
     * @param major (range:0~65535)
     * @param minor (range:0~65535)
     */
    private void writeMajorMinor(int major, int minor, WriteCallback writeCallback) {
        writeCallbackHashMap.put(WRITE_MAJOR_MINOR, writeCallback);
        this.major = major;
        this.minor = minor;
        // 写入前判断是否加密
        if (beacon.isPasswordEnabled && !haveWritePassword) {
            writeCallback.onFailure(WRITE_NOT_PERMITTED);
            return;
        }
        byte[] majorMinor = new byte[4];
        // 校验 major 和 minor 是否合法
        if (!checkMajorMinorLegal(major, minor)) {
            writeCallback.onFailure(SETTING_DATA_INVALID);
            return;
        } else {
            // 数据合法,转换为字节数组
            convertMajorMinorToBytes(major, minor, majorMinor);
        }
        if (!bluetoothLEHelper.writeMajorMinor(majorMinor)) {
            writeCallback.onFailure(SERVICE_OR_CHARACTERISTIC_NOT_EXIST);
        }
    }

    /**
     * Reload the sensor data.Force to reload the sensoro data.
     */
    private void reloadSensorData(WriteCallback writeCallback) {
        writeCallbackHashMap.put(RELOAD_SENSOR_DATA, writeCallback);
        // 写入前判断是否加密
        if (beacon.isPasswordEnabled && !haveWritePassword) {
            writeCallback.onFailure(WRITE_NOT_PERMITTED);
            return;
        }
        if (!bluetoothLEHelper.reloadSensoroData()) {
            writeCallback.onFailure(SERVICE_OR_CHARACTERISTIC_NOT_EXIST);
        }
    }

    /**
     * Reload the sensor data.Force to reload the sensoro data.
     */
    public void reloadSensorData() {
        // 写入前判断是否加密
        if (beacon.isPasswordEnabled && !haveWritePassword) {
            callback.onReloadSensorData(beacon, WRITE_NOT_PERMITTED);
            return;
        }
        if (!bluetoothLEHelper.reloadSensoroData()) {
            callback.onReloadSensorData(beacon, FAILURE);
        }
    }

    /**
     * Disable iBeacon function of Yunzi.</br>
     * Added from SDK_VERION {@link com.sensoro.beacon.kit.SensoroBeaconManager#SDK_VERSION} 3.0.0, only support B0-3.0,C0-3.0 and above.
     */
    public void disableIBeacon() {
        if (!((beacon.hardwareModelName.equals(Beacon.HV_B0) && beacon.firmwareVersion.equals(Beacon.FV_30))
                || (beacon.hardwareModelName.equals(Beacon.HV_C0) && beacon.firmwareVersion.equals(Beacon.FV_30)))) {
            // 不是 B0-3.0,C0-3.0, 不支持设置
            callback.onDisableIBeacon(beacon, NOT_SUPPORT);
            return;
        }
        this.isIBeaconEnabled = false;
        if (beacon.isPasswordEnabled && !haveWritePassword) {
            callback.onDisableIBeacon(beacon, WRITE_NOT_PERMITTED);
            return;
        }

        byte[] disableIBeaconBytes = new byte[1];
        disableIBeaconBytes[0] = 0;

        if (!bluetoothLEHelper.enableIBeacon(disableIBeaconBytes)) {
            callback.onDisableIBeacon(beacon, FAILURE);
            return;
        }
    }

    /**
     * Disable iBeacon function of Yunzi.</br>
     * Added from SDK_VERION {@link com.sensoro.beacon.kit.SensoroBeaconManager#SDK_VERSION} 3.0.0, only support B0-3.0,C0-3.0 and above.
     */
    private void disableIBeacon(WriteCallback writeCallback) {
        writeCallbackHashMap.put(DISABLE_IBEACON, writeCallback);
        if (!((beacon.hardwareModelName.equals(Beacon.HV_B0) && beacon.firmwareVersion.equals(Beacon.FV_30))
                || (beacon.hardwareModelName.equals(Beacon.HV_C0) && beacon.firmwareVersion.equals(Beacon.FV_30)))) {
            // 不是 B0-3.0,C0-3.0, 不支持设置
            writeCallback.onFailure(NOT_SUPPORT);
            return;
        }
        this.isIBeaconEnabled = false;
        if (beacon.isPasswordEnabled && !haveWritePassword) {
            writeCallback.onFailure(WRITE_NOT_PERMITTED);
            return;
        }

        byte[] disableIBeaconBytes = new byte[1];
        disableIBeaconBytes[0] = 0;

        if (!bluetoothLEHelper.enableIBeacon(disableIBeaconBytes)) {
            writeCallback.onFailure(SERVICE_OR_CHARACTERISTIC_NOT_EXIST);
            return;
        }
    }

    /**
     * Enable iBeacon function of Yunzi.</br>
     * Added from SDK_VERION {@link com.sensoro.beacon.kit.SensoroBeaconManager#SDK_VERSION} 3.0.0, only support B0-3.0,C0-3.0 and above.
     */
    public void enableIBeacon() {
        if (!((beacon.hardwareModelName.equals(Beacon.HV_B0) && beacon.firmwareVersion.equals(Beacon.FV_30))
                || (beacon.hardwareModelName.equals(Beacon.HV_C0) && beacon.firmwareVersion.equals(Beacon.FV_30)))) {
            // 不是 B0-3.0,C0-3.0, 不支持设置
            callback.onEnableIBeacon(beacon, NOT_SUPPORT);
            return;
        }
        this.isIBeaconEnabled = true;
        if (beacon.isPasswordEnabled && !haveWritePassword) {
            callback.onEnableIBeacon(beacon, WRITE_NOT_PERMITTED);
            return;
        }

        byte[] enableIBeaconBytes = new byte[1];
        enableIBeaconBytes[0] = 1;

        if (!bluetoothLEHelper.enableIBeacon(enableIBeaconBytes)) {
            callback.onEnableIBeacon(beacon, FAILURE);
            return;
        }
    }

    /**
     * Enable iBeacon function of Yunzi.</br>
     * Added from SDK_VERION {@link com.sensoro.beacon.kit.SensoroBeaconManager#SDK_VERSION} 3.0.0, only support B0-3.0,C0-3.0 and above.
     */
    private void enableIBeacon(WriteCallback writeCallback) {
        writeCallbackHashMap.put(ENABLE_IBEACON, writeCallback);
        if (!((beacon.hardwareModelName.equals(Beacon.HV_B0) && beacon.firmwareVersion.equals(Beacon.FV_30))
                || (beacon.hardwareModelName.equals(Beacon.HV_C0) && beacon.firmwareVersion.equals(Beacon.FV_30)))) {
            // 不是 B0-3.0,C0-3.0, 不支持设置
            writeCallback.onFailure(NOT_SUPPORT);
            return;
        }
        this.isIBeaconEnabled = true;
        if (beacon.isPasswordEnabled && !haveWritePassword) {
            writeCallback.onFailure(WRITE_NOT_PERMITTED);
            return;
        }

        byte[] enableIBeaconBytes = new byte[1];
        enableIBeaconBytes[0] = 1;

        if (!bluetoothLEHelper.enableIBeacon(enableIBeaconBytes)) {
            writeCallback.onFailure(SERVICE_OR_CHARACTERISTIC_NOT_EXIST);
            return;
        }
    }

    /**
     * Write the broadcast key of Yunzi.</br>
     * Added from SDK_VERION {@link com.sensoro.beacon.kit.SensoroBeaconManager#SDK_VERSION} 3.0.0, only support B0-3.0,C0-3.0 and above.
     *
     * @param broadcastKey The value of broadcast key.
     */
    public void writeBroadcastKey(String broadcastKey) {
        if (!((beacon.hardwareModelName.equals(Beacon.HV_B0) && beacon.firmwareVersion.equals(Beacon.FV_30))
                || (beacon.hardwareModelName.equals(Beacon.HV_C0) && beacon.firmwareVersion.equals(Beacon.FV_30)))) {
            // 不是 B0-3.0,C0-3.0, 不支持设置
            callback.onWriteBroadcastKey(beacon, NOT_SUPPORT);
            return;
        }
        if (beacon.isPasswordEnabled && !haveWritePassword) {
            callback.onWriteBroadcastKey(beacon, WRITE_NOT_PERMITTED);
            return;
        }

        byte[] broadcastKeyBytes = null;
        // 校验 broadcast key 是否合法
        if (broadcastKey == null || !(broadcastKey != null && (broadcastKey.length() == 40 || broadcastKey.length() == 90))) {
            callback.onWriteBroadcastKey(beacon, SETTING_DATA_INVALID);
            return;
        } else {
            // 数据合法
            int keyLength = broadcastKey.length();
            if (keyLength == 90){
                String decrypt = SensoroUtils.decrypt_AES_256(broadcastKey.substring(2, keyLength), BROADCAST_KEY_SECRET);
                if (decrypt == null) {
                    callback.onWriteBroadcastKey(beacon, SETTING_DATA_INVALID);
                    return;
                } else {
                    long expiryDate = Long.valueOf(Integer.parseInt(decrypt.substring(40, decrypt.length()), 16));
                    long currentDate = System.currentTimeMillis();
                    if (currentDate > expiryDate * 1000) {
                        callback.onWriteBroadcastKey(beacon, SETTING_DATA_INVALID);
                        return;
                    } else {
                        broadcastKey = decrypt.substring(0,40);
                    }
                }
            }

            // 写入是否全0
            Pattern pattern = Pattern.compile("[0]{40}");
            Matcher matcher = pattern.matcher(broadcastKey);
            if (matcher.matches()) {
                isKeyZero = true;
            } else {
                isKeyZero = false;
            }

            // 转换为字节数组
            broadcastKeyBytes = SensoroUtils.HexString2Bytes(broadcastKey);
        }
        if (broadcastKeyBytes != null && broadcastKeyBytes.length == 20) {
            if (!bluetoothLEHelper.writeBroadcastKey(broadcastKeyBytes)) {
                callback.onWriteBroadcastKey(beacon, FAILURE);
                return;
            }
        } else {
            callback.onWriteBroadcastKey(beacon, SETTING_DATA_INVALID);
        }
    }

    /**
     * Write the broadcast key of Yunzi.</br>
     * Added from SDK_VERION {@link com.sensoro.beacon.kit.SensoroBeaconManager#SDK_VERSION} 3.0.0, only support B0-3.0,C0-3.0 and above.
     *
     * @param broadcastKey The value of broadcast key.
     */
    private void writeBroadcastKey(String broadcastKey, WriteCallback writeCallback) {
        writeCallbackHashMap.put(WRITE_BROADCAST_KEY, writeCallback);
        if (!((beacon.hardwareModelName.equals(Beacon.HV_B0) && beacon.firmwareVersion.equals(Beacon.FV_30))
                || (beacon.hardwareModelName.equals(Beacon.HV_C0) && beacon.firmwareVersion.equals(Beacon.FV_30)))) {
            // 不是 B0-3.0,C0-3.0, 不支持设置
            writeCallback.onFailure(NOT_SUPPORT);
            return;
        }
        if (beacon.isPasswordEnabled && !haveWritePassword) {
            writeCallback.onFailure(WRITE_NOT_PERMITTED);
            return;
        }

        // 写入是否全0
        Pattern pattern = Pattern.compile("[0]{40}");
        Matcher matcher = pattern.matcher(broadcastKey);
        if (matcher.matches()) {
            isKeyZero = true;
        } else {
            isKeyZero = false;
        }

        byte[] broadcastKeyBytes = null;
        // 校验 broadcast key 是否合法
        if (broadcastKey == null || (broadcastKey != null && broadcastKey.length() != 40)) {
            writeCallback.onFailure(SETTING_DATA_INVALID);
            return;
        } else {
            // 数据合法,转换为字节数组
            broadcastKeyBytes = SensoroUtils.HexString2Bytes(broadcastKey);
        }
        if (broadcastKeyBytes != null && broadcastKeyBytes.length == 20) {
            if (!bluetoothLEHelper.writeBroadcastKey(broadcastKeyBytes)) {
                writeCallback.onFailure(SERVICE_OR_CHARACTERISTIC_NOT_EXIST);
                return;
            }
        } else {
            writeCallback.onFailure(SETTING_DATA_INVALID);
        }
    }

    /**
     * Write the secure broadcast interval of the beacon.</br>
     * {@link com.sensoro.beacon.kit.BaseSettings.SecureBroadcastInterval#NONE} only effect on B0-2.3.
     *
     * @param secureBroadcastInterval The value of {@link SecureBroadcastInterval} .
     */
    public void writeSecureBroadcastInterval(SecureBroadcastInterval secureBroadcastInterval) {
        // 不是 B0-2.3 和 B0-3.0,C0-3.0
        if (!((beacon.hardwareModelName.equals(Beacon.HV_B0) && beacon.firmwareVersion.equals(Beacon.FV_23))
                || (beacon.hardwareModelName.equals(Beacon.HV_B0) && beacon.firmwareVersion.equals(Beacon.FV_30))
                || (beacon.hardwareModelName.equals(Beacon.HV_C0) && beacon.firmwareVersion.equals(Beacon.FV_30)))) {
            callback.onWriteSecureBroadcastInterval(beacon, NOT_SUPPORT);
            return;
        }
        // 给非B0-2.3设置NONE
        if (secureBroadcastInterval == SecureBroadcastInterval.NONE && !(beacon.hardwareModelName.equals(Beacon.HV_B0) && beacon.firmwareVersion.equals(Beacon.FV_23))){
            callback.onWriteSecureBroadcastInterval(beacon, SETTING_DATA_INVALID);
            return;
        }

        this.secureBroadcastInterval = secureBroadcastInterval;
        // 写入前判断是否加密
        if (beacon.isPasswordEnabled && !haveWritePassword) {
            callback.onWriteSecureBroadcastInterval(beacon, WRITE_NOT_PERMITTED);
            return;
        }

        byte[] secureBroadcastIntervalBytes = new byte[20];
        // 校验 rotation 是否合法
        if (secureBroadcastInterval == SecureBroadcastInterval.UNKNOWN) {
            callback.onWriteSecureBroadcastInterval(beacon, SETTING_DATA_INVALID);
            return;
        } else {
            // 数据合法,转换为字节数组
            convertSecureBroadcastIntervalToBytes(secureBroadcastInterval, secureBroadcastIntervalBytes);
        }
        if (!bluetoothLEHelper.writeSecureBroadcastInterval(secureBroadcastIntervalBytes)) {
            callback.onWriteSecureBroadcastInterval(beacon, FAILURE);
            return;
        }
    }

    /**
     * Write the secure broadcast interval of the beacon.</br>
     * {@link com.sensoro.beacon.kit.BaseSettings.SecureBroadcastInterval#NONE} only effect on B0-2.3.
     *
     * @param secureBroadcastInterval The value of {@link SecureBroadcastInterval} .
     */
    private void writeSecureBroadcastInterval(SecureBroadcastInterval secureBroadcastInterval, WriteCallback writeCallback) {
        // 不是 B0-2.3 和 B0-3.0,C0-3.0
        writeCallbackHashMap.put(WRITE_SECURE_BROADCAST_INTERVAL, writeCallback);
        if (!((beacon.hardwareModelName.equals(Beacon.HV_B0) && beacon.firmwareVersion.equals(Beacon.FV_23))
                || (beacon.hardwareModelName.equals(Beacon.HV_B0) && beacon.firmwareVersion.equals(Beacon.FV_30))
                || (beacon.hardwareModelName.equals(Beacon.HV_C0) && beacon.firmwareVersion.equals(Beacon.FV_30)))) {

            writeCallback.onFailure(NOT_SUPPORT);
            return;
        }
        // 给非B0-2.3设置NONE
        if (secureBroadcastInterval == SecureBroadcastInterval.NONE && !(beacon.hardwareModelName.equals(Beacon.HV_B0) && beacon.firmwareVersion.equals(Beacon.FV_23))){
            writeCallback.onFailure(SETTING_DATA_INVALID);
            return;
        }

        this.secureBroadcastInterval = secureBroadcastInterval;
        // 写入前判断是否加密
        if (beacon.isPasswordEnabled && !haveWritePassword) {
            writeCallback.onFailure(WRITE_NOT_PERMITTED);
            return;
        }

        byte[] secureBroadcastIntervalBytes = new byte[20];
        // 校验 rotation 是否合法
        if (secureBroadcastInterval == SecureBroadcastInterval.UNKNOWN) {
            writeCallback.onFailure(SETTING_DATA_INVALID);
            return;
        } else {
            // 数据合法,转换为字节数组
            convertSecureBroadcastIntervalToBytes(secureBroadcastInterval, secureBroadcastIntervalBytes);
        }
        if (!bluetoothLEHelper.writeSecureBroadcastInterval(secureBroadcastIntervalBytes)) {
            writeCallback.onFailure(SERVICE_OR_CHARACTERISTIC_NOT_EXIST);
            return;
        }
    }

    /**
     *  Flash light of Yunzi major and minor of this beacon.
     *  This method only supports C0-3.1 and above.
     *
     *  @param repeat  If the bit of command is 1, then this light will turn on one times, if 0 ,the light turn off.
     *                  you can use custom command, or use {@link com.sensoro.beacon.kit.SensorSettings.FlashLightCommand}
     *  @param repeatCount  The count that command was repeat.
     *
     *  @return Can this writing operation be executed.
     */
    public void flashLightWitCommand(byte repeat,byte repeatCount) {
        if (!((beacon.hardwareModelName.equals(Beacon.HV_C0) && beacon.firmwareVersion.equals(Beacon.FV_31)))) {
            // 不是 C0-3.1, 不支持设置
            callback.onFlashLightWitCommand(beacon, NOT_SUPPORT);
            return;
        }
        if (beacon.isPasswordEnabled && !haveWritePassword) {
            callback.onFlashLightWitCommand(beacon, WRITE_NOT_PERMITTED);
            return;
        }

        byte[] cmdLEDBytes = new byte[5];
        createLEDBytes(repeat,repeatCount,cmdLEDBytes);

        if (!bluetoothLEHelper.onFlashLightWitCommand(cmdLEDBytes)) {
            callback.onEnableIBeacon(beacon, FAILURE);
            return;
        }

    }

    /**
     * Get base settings of beacon
     */
    protected BaseSettings getBaseSettings() {
        return baseSettings;
    }

    /**
     * Get sensor settings of beacon
     */
    protected SensorSettings getSensorSettings() {
        return sensorSettings;
    }

    /**
     * Check whether the BaseSettings is valid.
     *
     * @param baseSettings
     * @return
     */
    private boolean checkBaseSettingsLegal(BaseSettings baseSettings) {
        // 校验 txPower 合法性 0~7
        BaseSettings.TransmitPower transmitPower = baseSettings.getTransmitPower();
        if (BaseSettings.TransmitPower.UNKNOWN == transmitPower) {
            // 用户没有设置功率，则设置之前读取的功率
            baseSettings.setTransmitPower(this.baseSettings.getTransmitPower());
        } else {
            if (isA0Beacon() && (transmitPower.compareTo(BaseSettings.TransmitPower.LEVEL2) > 0)) {
                // A0 只有 3 档
                return false;
            } else if (isB0Beacon() && (transmitPower.compareTo(BaseSettings.TransmitPower.LEVEL7) > 0)) {
                // B0 只有 8 档
                return false;
            }
        }
        // 校验广播频率合性 0~9
        if (BaseSettings.AdvertisingInterval.UNKNOWN == baseSettings.getAdvertisingInterval()) {
            baseSettings.setAdvertisingInterval(this.baseSettings.getAdvertisingInterval());
        }
        // 校验省电模式合法性
        if (BaseSettings.EnergySavingMode.UNKNOWN == baseSettings.getEnergySavingMode()) {
            baseSettings.setEnergySavingMode(this.baseSettings.getEnergySavingMode());
        }
        // 校验一米处测量值是否合性 -128~127
        if (BaseSettings.MESURED_POWER_UNKNOW == baseSettings.getMeasuredPower()) {
            baseSettings.setMeasuredPower(this.baseSettings.getMeasuredPower());
        } else if (BaseSettings.MIN___128 > baseSettings.getMeasuredPower() && baseSettings.getMeasuredPower() > BaseSettings.MAX_127) {
            return false;
        }
        writeBaseSettings = baseSettings;
        return true;
    }

    /**
     * 校验 SensoSettings 合法性
     *
     * @param sensorSettings
     * @return
     */
    private boolean checkSensoSettingsLegal(SensorSettings sensorSettings) {
        // 校验温度采样间隔 0x00,1000~65535
        int temperatureSamplingInterval = sensorSettings.getTemperatureSamplingInterval();
        if (temperatureSamplingInterval == SensorSettings.DATA_DEFAULT) {
            sensorSettings.setTemperatureSamplingInterval(this.sensorSettings.getTemperatureSamplingInterval());
        } else if (temperatureSamplingInterval != 0 && (SensorSettings.SENSO_DATA_MIN > temperatureSamplingInterval || temperatureSamplingInterval > SensorSettings.SENSO_DATA_MAX)) {
            return false;
        }
        // 校验光感采样间隔 0x00,1000~65535
        int brightnessSamplingInterval = sensorSettings.getLightSamplingInterval();
        if (brightnessSamplingInterval == SensorSettings.DATA_DEFAULT) {
            sensorSettings.setLightSamplingInterval(this.sensorSettings.getLightSamplingInterval());
        } else if (brightnessSamplingInterval != 0 && (SensorSettings.SENSO_DATA_MIN > brightnessSamplingInterval || brightnessSamplingInterval > SensorSettings.SENSO_DATA_MAX)) {
            return false;
        }
        // 校验加速度计数器灵敏度
        if (SensorSettings.AccelerometerSensitivity.UNKNOWN == sensorSettings.getAccelerometerSensitivity()) {
            sensorSettings.setAccelerometerSensitivity(this.sensorSettings.getAccelerometerSensitivity());
        }
        writeSensorSettings = sensorSettings;
        return true;
    }

    /**
     * 校验 UUID 合法性
     *
     * @param uuid
     * @return
     */
    private boolean checkUUIDLegal(String uuid) {
        // 长度不满足
        if (uuid.length() != BaseSettings.UUID_STRING_LENGTH) {
            return false;
        }
        // 中间第 7,14,19,24 位不为 '-'
        if (uuid.charAt(8) != '-' && uuid.charAt(13) != '-' && uuid.charAt(18) != '-' && uuid.charAt(23) != '-') {
            return false;
        }

        // 校验是否包含非 '0~f' 和 '-' 以外的字符
        uuid = uuid.toLowerCase(); // 字母全部转换小写
        for (int i = 0; i < uuid.length(); i++) {
            if (uuid.charAt(i) >= 'a' && uuid.charAt(i) <= 'f' || uuid.charAt(i) <= '9' && uuid.charAt(i) >= '0' || uuid.charAt(i) == '-') {
                continue;
            } else {
                return false;
            }
        }

        return true;
    }

    /**
     * 校验 major 和 minor 合法性
     *
     * @param major
     * @param minor
     * @return
     */
    private boolean checkMajorMinorLegal(int major, int minor) {
        if (major > BaseSettings.MAX_65535 || major < BaseSettings.MIN_0 || minor > BaseSettings.MAX_65535 || minor < BaseSettings.MIN_0) {
            return false;
        }
        return true;
    }

    /**
     * 将写入 BaseSetting 转换为字节数组
     *
     * @param baseSettings
     * @param newBaseSettings
     */
    private void convertBaseSettingsToBytes(BaseSettings baseSettings, byte[] newBaseSettings) {
        // 拷贝功率
        byte transmitPowerByte = getTransmitPowerByte(baseSettings.getTransmitPower());
        newBaseSettings[0] = transmitPowerByte;
        // 拷贝频率
        byte advertisingIntervalByte = getAdvertisingIntervalByte(baseSettings.getAdvertisingInterval());
        newBaseSettings[1] = advertisingIntervalByte;
        // 拷贝省电模式
        byte energySavingModeByte = getEnergySavingModeByte(baseSettings.getEnergySavingMode());
        newBaseSettings[2] = energySavingModeByte;
        // 拷贝一米处测量值
        if (baseSettings.getMeasuredPower() != BaseSettings.MESURED_POWER_UNKNOW) {
            newBaseSettings[3] = (byte) baseSettings.getMeasuredPower();
        } else {
            newBaseSettings[3] = (byte) this.baseSettings.getMeasuredPower();
        }

        // A0 型号 beacon 需要写入广播信道
        if (isA0Beacon()) {
            newBaseSettings[4] = 0x00;
            newBaseSettings[5] = 0x07;
        }
    }

    private byte getAdvertisingIntervalByte(BaseSettings.AdvertisingInterval advertisingInterval) {
        switch (advertisingInterval) {
            case ADVERTISING_INTERVAL_100:
                return 0;
            case ADVERTISING_INTERVAL_152_5:
                return 1;
            case ADVERTISING_INTERVAL_211_25:
                return 2;
            case ADVERTISING_INTERVAL_318_75:
                return 3;
            case ADVERTISING_INTERVAL_417_5:
                return 4;
            case ADVERTISING_INTERVAL_546_25:
                return 5;
            case ADVERTISING_INTERVAL_760:
                return 6;
            case ADVERTISING_INTERVAL_852_5:
                return 7;
            case ADVERTISING_INTERVAL_1285:
                return 9;
            default: // 传入数值错误默认频率最低
                return 9;
        }
    }

    private byte getEnergySavingModeByte(BaseSettings.EnergySavingMode energySavingMode) {
        switch (energySavingMode) {
            case NONE:
                return 0;
            case LIGHT_SENSOR:
                return 1;
            default:
                return 0;
        }
    }

    private byte getTransmitPowerByte(BaseSettings.TransmitPower transmitPower) {
        switch (transmitPower) {
            case LEVEL0:
                return 0;
            case LEVEL1:
                return 1;
            case LEVEL2:
                return 2;
            case LEVEL3:
                return 3;
            case LEVEL4:
                return 4;
            case LEVEL5:
                return 5;
            case LEVEL6:
                return 6;
            case LEVEL7:
                return 7;
            case LEVEL8:
                return 8;
            case LEVEL9:
                return 9;
            case LEVEL10:
                return 10;
            case LEVEL11:
                return 11;
            default:
                return 0;
        }
    }

    /**
     * SensoSettings 转换为字节数组
     *
     * @param sensorSettings
     * @param newSensoSettings
     */
    private void convertSensoSettingsToBytes(SensorSettings sensorSettings, byte[] newSensoSettings) {
        // 拷贝温度传感器采样间隔
        if (sensorSettings.getTemperatureSamplingInterval() != SensorSettings.DATA_DEFAULT) {
            newSensoSettings[0] = (byte) (sensorSettings.getTemperatureSamplingInterval() & 0xff);
            newSensoSettings[1] = (byte) ((sensorSettings.getTemperatureSamplingInterval() >> 8) & 0xff);
        } else {
            newSensoSettings[0] = (byte) (this.sensorSettings.getTemperatureSamplingInterval() & 0xff);
            newSensoSettings[1] = (byte) ((this.sensorSettings.getTemperatureSamplingInterval() >> 8) & 0xff);
        }

        // 拷贝光线传感器采样间隔
        if (sensorSettings.getLightSamplingInterval() != SensorSettings.DATA_DEFAULT) {
            newSensoSettings[2] = (byte) (sensorSettings.getLightSamplingInterval() & 0xff);
            newSensoSettings[3] = (byte) ((sensorSettings.getLightSamplingInterval() >> 8) & 0xff);
        } else {
            newSensoSettings[2] = (byte) (this.sensorSettings.getLightSamplingInterval() & 0xff);
            newSensoSettings[3] = (byte) ((this.sensorSettings.getLightSamplingInterval() >> 8) & 0xff);
        }

        // 加速度传感器灵敏度
        byte accelerometerSensitivityByte = getAccelerometerSensitivityByte(sensorSettings.getAccelerometerSensitivity());
        newSensoSettings[4] = accelerometerSensitivityByte;
        newSensoSettings[5] = 0;
    }

    private byte getAccelerometerSensitivityByte(SensorSettings.AccelerometerSensitivity accelerometerSensitivityKey) {
        switch (accelerometerSensitivityKey) {
            case DISABLED:
                return 0;
            case MIN:
                return 0x70;
            case MEDIUM:
                return 0x5d;
            case MAX:
                return 0x4b;
            default:
                return 0;
        }
    }

    /**
     * 生成 LED 序列命令
     * @param cmdLEDBytes
     */
    private void createLEDBytes(byte sequence,byte cycle,byte[] cmdLEDBytes) {
        cmdLEDBytes[0] = 0x04;
        cmdLEDBytes[1] = 0x00;
        cmdLEDBytes[2] = 0x00;
        cmdLEDBytes[3] = sequence;
        cmdLEDBytes[4] = cycle;
    }

    /**
     * 将UUID转换为字节数组
     *
     * @param uuid
     * @param newUuidBytes
     * @return
     */
    private boolean convertUUIDToBytes(String uuid, byte[] newUuidBytes) {
        uuid = uuid.replace("-", ""); // 去掉 uuid 中的 '-'
        byte[] uuidBytes = SensoroUtils.HexString2Bytes(uuid);
        if (uuidBytes == null) {
            return false;
        }
        System.arraycopy(uuidBytes, 0, newUuidBytes, 0, uuidBytes.length);

        return true;
    }

    private void convertMajorMinorToBytes(int major, int minor, byte[] majorMinor) {
        majorMinor[0] = (byte) ((major >> 8) & 0xff);
        majorMinor[1] = (byte) (major & 0xff);
        majorMinor[2] = (byte) ((minor >> 8) & 0xff);
        majorMinor[3] = (byte) (minor & 0xff);
    }

    private void convertSecureBroadcastIntervalToBytes(SecureBroadcastInterval secureBroadcastInterval, byte[] secureBroadcastIntervalBytes) {
        int secureBroadcastIntervalInt = SensoroUtils.getSecureBroadcastIntervalInt(secureBroadcastInterval);
        secureBroadcastIntervalBytes[0] = (byte) (secureBroadcastIntervalInt & 0xff);
        secureBroadcastIntervalBytes[1] = (byte) ((secureBroadcastIntervalInt >> 8) & 0xff);
        secureBroadcastIntervalBytes[2] = (byte) ((secureBroadcastIntervalInt >> 16) & 0xff);
        secureBroadcastIntervalBytes[3] = (byte) ((secureBroadcastIntervalInt >> 24) & 0xff);
    }

    private boolean isB0Above2_3() {
        if (beaconType.equals(Beacon.HV_B0) && (beaconFirmware.compareTo(Beacon.FV_22) > 0)) {
            return true;
        } else {
            return false;
        }
    }

    private boolean isA0Beacon() {
        return beaconType.equals(Beacon.HV_A0);
    }

    private boolean isB0Beacon() {
        return beaconType.equals(Beacon.HV_B0);
    }

    private boolean isC0Beacon() {
        return beaconType.equals(Beacon.HV_C0);
    }

    private BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() {
        /**
         * BLE 连接状态回调
         */
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            // 连接状态回调，停止此次连接超时计时
            handler.removeCallbacks(timeOutRunnable);
            handler.removeCallbacks(connectTimeOutRunnable);
            if (SensoroBeaconManager.DEBUG) {
                Log.d(TAG, "connect timeout---stop time out runnable");
            }

            if (status == BluetoothGatt.GATT_SUCCESS) {
                if (newState == BluetoothProfile.STATE_CONNECTED) {
                    // 连接 beacon 成功,获取 beacon 中的服务
                    gatt.discoverServices();
                }
                if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                    isConnected = false;
                    callback.onConnectedState(beacon, Beacon.DISCONNECTED, SUCCESS);
                    connectCallback.onConnectedState(ConnectCallback.STATE_DISCONNECTED, ConnectCallback.SUCCESS);
                    if (SensoroBeaconManager.DEBUG) {
                        Log.d(TAG, "onConnectionStateChange---callback disconnect success");
                    }
                }
            } else if (status == BluetoothGatt.GATT_FAILURE){
                isConnected = false;
                if (newState == BluetoothProfile.STATE_CONNECTED) {
                    callback.onConnectedState(beacon, Beacon.CONNECTED, FAILURE);
                    connectCallback.onConnectedState(ConnectCallback.STATE_CONNECTED, ConnectCallback.FAILURE);
                    if (SensoroBeaconManager.DEBUG) {
                        Log.d(TAG, "onConnectionStateChange---callback connect failure");
                    }
                }
                if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                    callback.onConnectedState(beacon, Beacon.DISCONNECTED, FAILURE);
                    connectCallback.onConnectedState(ConnectCallback.STATE_DISCONNECTED, ConnectCallback.FAILURE);
                    if (SensoroBeaconManager.DEBUG) {
                        Log.d(TAG, "onConnectionStateChange---callback disconnect failure");
                    }
                }
            } else if (status == 133) {
                isConnected = false;
                callback.onConnectedState(beacon, Beacon.CONNECTED, SensoroBeaconConnection.FAILURE);
                connectCallback.onConnectedState(ConnectCallback.STATE_CONNECTED, ConnectCallback.FAILURE);
            } else {
                isConnected = false;
                if (newState == BluetoothProfile.STATE_CONNECTED) {
                    callback.onConnectedState(beacon, Beacon.CONNECTED, status);
                    connectCallback.onConnectedState(ConnectCallback.STATE_CONNECTED, ConnectCallback.FAILURE);
                    if (SensoroBeaconManager.DEBUG) {
                        Log.d(TAG, "onConnectionStateChange---callback connect failure");
                    }
                }
                if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                    callback.onConnectedState(beacon, Beacon.DISCONNECTED, DISCONNECTED_BY_REMOTE_DEVICE);
                    connectCallback.onConnectedState(ConnectCallback.STATE_DISCONNECTED, ConnectCallback.FAILURE);
                    if (SensoroBeaconManager.DEBUG) {
                        Log.d(TAG, "onConnectionStateChange---callback disconnect failure");
                    }
                }
            }
            super.onConnectionStateChange(gatt, status, newState);
        }

        /**
         * BLE 搜索服务回调
         *
         * @param gatt
         * @param status
         */
        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            // 获取服务成功,读取 Base 和 Sensor 的设置
            if (status == BluetoothGatt.GATT_SUCCESS) {
                List<BluetoothGattService> gattServiceList = gatt.getServices();
                if (bluetoothLEHelper.checkGattServices(beaconFirmware,beaconType, gattServiceList)) {
                    if (!bluetoothLEHelper.getBaseSettings()) {
                        // 没有找到 BaseSettings 的特征值
                        callback.onConnectedState(beacon, Beacon.CONNECTED, FAILURE);
                        connectCallback.onConnectedState(ConnectCallback.STATE_CONNECTED, ConnectCallback.FAILURE);
                        gatt.close();
                        if (SensoroBeaconManager.DEBUG) {
                            Log.d(TAG, "onServicesDiscovered---callback connect failure:no baseSetting char");
                        }
                    }
                } else {
                    // 没有找到 BaseSettings 或者 SensoSettings 服务
                    callback.onConnectedState(beacon, Beacon.CONNECTED, FAILURE);
                    connectCallback.onConnectedState(ConnectCallback.STATE_CONNECTED, ConnectCallback.FAILURE);
                    gatt.close();
                    if (SensoroBeaconManager.DEBUG) {
                        Log.d(TAG, "onServicesDiscovered---callback connect failure:no baseSetting or sensorSetting service");
                    }
                }
            } else {
                // 读取 Beacon 服务失败,断开连接
                callback.onConnectedState(beacon, Beacon.CONNECTED, FAILURE);
                connectCallback.onConnectedState(ConnectCallback.STATE_CONNECTED, ConnectCallback.FAILURE);
                gatt.close();
                if (SensoroBeaconManager.DEBUG) {
                    Log.d(TAG, "onServicesDiscovered---callback connect failure:get beacon service failure");
                }
            }
            super.onServicesDiscovered(gatt, status);
        }

        /**
         * BLE 写特征值回调
         *
         * @param gatt
         * @param characteristic
         * @param status
         */
        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            if (characteristic.getUuid().equals(BluetoothLEHelper.GattInfo.SENSO_ACCELERATOR_UUID)) {
                // 加速度计数器置0
                WriteCallback writeCallback = writeCallbackHashMap.get(RESET_ACCELEROMETER_COUNT);
                if (status == BluetoothGatt.GATT_SUCCESS) {
                    callback.onResetAcceleratorCount(beacon, SUCCESS);
//                    writeCallback.onSuccess();
                    writeCallbackHashMap.remove(RESET_ACCELEROMETER_COUNT);
                } else {
                    callback.onResetAcceleratorCount(beacon, FAILURE);
//                    writeCallback.onFailure(FAILURE);
                    writeCallbackHashMap.remove(RESET_ACCELEROMETER_COUNT);
                }
            } else if (characteristic.getUuid().equals(BluetoothLEHelper.GattInfo.BASE_CHANGE_PWD_UUID)) {
                // 更新写入密码
                WriteCallback writeCallbackDisable = writeCallbackHashMap.get(DISABLE_PASSWORD);
                WriteCallback writeCallbackString = writeCallbackHashMap.get(WRITE_PASSWORD_STRING);
                WriteCallback writeCallbackByte = writeCallbackHashMap.get(WRITE_PASSWORD_BYTE);
                if (status == BluetoothGatt.GATT_SUCCESS) {
                    if (isDisabledPassword) {
                        isDisabledPassword = false;
                        callback.onDisablePassword(beacon, SUCCESS);
//                        writeCallbackDisable.onSuccess();
                        writeCallbackHashMap.remove(DISABLE_PASSWORD);
                    } else {
                        callback.onWritePassword(beacon, SUCCESS);
                        if (writeCallbackByte != null) {
//                            writeCallbackByte.onSuccess();
                            writeCallbackHashMap.remove(WRITE_PASSWORD_BYTE);
                        }
                        if (writeCallbackString != null) {
//                            writeCallbackString.onSuccess();
                            writeCallbackHashMap.remove(WRITE_PASSWORD_STRING);
                        }
                    }
                } else {
                    if (isDisabledPassword) {
                        isDisabledPassword = false;
                        callback.onDisablePassword(beacon, FAILURE);
//                        writeCallbackDisable.onFailure(FAILURE);
                        writeCallbackHashMap.remove(DISABLE_PASSWORD);
                    } else {
                        callback.onWritePassword(beacon, FAILURE);
                        if (writeCallbackByte != null) {
//                            writeCallbackByte.onFailure(FAILURE);
                            writeCallbackHashMap.remove(WRITE_PASSWORD_BYTE);
                        }
                        if (writeCallbackString != null) {
                            writeCallbackString.onFailure(FAILURE);
                            writeCallbackHashMap.remove(WRITE_PASSWORD_STRING);
                        }
                    }
                }
            } else if (characteristic.getUuid().equals(BluetoothLEHelper.GattInfo.BASE_CHECK_PWD_UUID)) {
                WriteCallback writeCallbackString = writeCallbackHashMap.get(REQUIRE_WRITE_PERMISSION_STRING);
                WriteCallback writeCallbackByte = writeCallbackHashMap.get(REQUIRE_WRITE_PERMISSION_BYTE);
                // 校验写入密码
                if (status == BluetoothGatt.GATT_SUCCESS) {
                    haveWritePassword = true;
                    callback.onRequireWritePermission(beacon, SUCCESS);
                    if (writeCallbackByte != null) {
                        writeCallbackByte.onSuccess();
                        writeCallbackHashMap.remove(REQUIRE_WRITE_PERMISSION_BYTE);
                    }
                    if (writeCallbackString != null) {
                        writeCallbackString.onSuccess();
                        writeCallbackHashMap.remove(REQUIRE_WRITE_PERMISSION_STRING);
                    }
                } else if (status == BluetoothGatt.GATT_WRITE_NOT_PERMITTED) {
                    callback.onRequireWritePermission(beacon, AUTHORIZATION_FAILED);
                    if (writeCallbackByte != null) {
                        writeCallbackByte.onFailure(AUTHORIZATION_FAILED);
                        writeCallbackHashMap.remove(REQUIRE_WRITE_PERMISSION_BYTE);
                    }
                    if (writeCallbackString != null) {
                        writeCallbackString.onFailure(AUTHORIZATION_FAILED);
                        writeCallbackHashMap.remove(REQUIRE_WRITE_PERMISSION_STRING);
                    }
                } else {
                    callback.onRequireWritePermission(beacon, FAILURE);
                    if (writeCallbackByte != null) {
                        writeCallbackByte.onFailure(FAILURE);
                        writeCallbackHashMap.remove(REQUIRE_WRITE_PERMISSION_BYTE);
                    }
                    if (writeCallbackString != null) {
                        writeCallbackString.onFailure(FAILURE);
                        writeCallbackHashMap.remove(REQUIRE_WRITE_PERMISSION_STRING);
                    }
                }
            } else if (characteristic.getUuid().equals(BluetoothLEHelper.GattInfo.BASE_PARAMS_SETTINGS_UUID)) {
                // 写入 BaseSettings
                WriteCallback writeCallbackByte = writeCallbackHashMap.get(WRITE_BASE_SETTINGS);
                if (status == BluetoothGatt.GATT_SUCCESS) {
                    baseSettings = writeBaseSettings;
                    beacon.baseSettings = writeBaseSettings;
                    callback.onWriteBaseSetting(beacon, SUCCESS);
                    writeCallbackByte.onSuccess();
                    writeCallbackHashMap.remove(WRITE_BASE_SETTINGS);
                } else {
                    callback.onWriteBaseSetting(beacon, FAILURE);
                    writeCallbackByte.onFailure(FAILURE);
                    writeCallbackHashMap.remove(WRITE_BASE_SETTINGS);
                }
            } else if (characteristic.getUuid().equals(BluetoothLEHelper.GattInfo.SENSO_PARAMS_SETTINGS_UUID)) {
                // 写入 SensoSettings
                WriteCallback writeCallbackByte = writeCallbackHashMap.get(WRITE_SENSOR_SETTINGS);
                if (status == BluetoothGatt.GATT_SUCCESS) {
                    sensorSettings = writeSensorSettings;
                    beacon.sensorSettings = writeSensorSettings;
                    callback.onWirteSensorSetting(beacon, SUCCESS);
                    writeCallbackByte.onSuccess();
                    writeCallbackHashMap.remove(WRITE_SENSOR_SETTINGS);
                } else {
                    callback.onWirteSensorSetting(beacon, FAILURE);
                    writeCallbackByte.onFailure(FAILURE);
                    writeCallbackHashMap.remove(WRITE_SENSOR_SETTINGS);
                }
            } else if (characteristic.getUuid().equals(BluetoothLEHelper.GattInfo.BASE_UUID_UUID)) {
                // 写入 UUID
                WriteCallback writeCallbackByte = writeCallbackHashMap.get(WRITE_PROXIMITY_UUID);
                if (status == BluetoothGatt.GATT_SUCCESS) {
                    beacon.proximityUUID = uuid;
                    callback.onWriteProximityUUID(beacon, SUCCESS);
                    writeCallbackByte.onSuccess();
                    writeCallbackHashMap.remove(WRITE_PROXIMITY_UUID);
                } else {
                    callback.onWriteProximityUUID(beacon, FAILURE);
                    writeCallbackByte.onFailure(FAILURE);
                    writeCallbackHashMap.remove(WRITE_PROXIMITY_UUID);
                }
            } else if (characteristic.getUuid().equals(BluetoothLEHelper.GattInfo.BASE_MAJOR_MINOR_UUID)) {
                // 写入 major 和 minor
                WriteCallback writeCallbackByte = writeCallbackHashMap.get(WRITE_MAJOR_MINOR);
                if (status == BluetoothGatt.GATT_SUCCESS) {
                    beacon.major = major;
                    beacon.minor = minor;
                    callback.onWriteMajorMinor(beacon, SUCCESS);
                    writeCallbackByte.onSuccess();
                    writeCallbackHashMap.remove(WRITE_MAJOR_MINOR);
                } else {
                    callback.onWriteMajorMinor(beacon, FAILURE);
                    writeCallbackByte.onFailure(FAILURE);
                    writeCallbackHashMap.remove(WRITE_MAJOR_MINOR);
                }
            } else if (characteristic.getUuid().equals(BluetoothLEHelper.GattInfo.SENSO_FORCE_UPDATE_UUID)) {
                WriteCallback writeCallbackByte = writeCallbackHashMap.get(RELOAD_SENSOR_DATA);
                // 强制更新 Sensor
                if (status == BluetoothGatt.GATT_SUCCESS) {
                    callback.onReloadSensorData(beacon, SUCCESS);
                    writeCallbackByte.onSuccess();
                    writeCallbackHashMap.remove(RELOAD_SENSOR_DATA);
                } else {
                    callback.onReloadSensorData(beacon, FAILURE);
                    writeCallbackByte.onFailure(FAILURE);
                    writeCallbackHashMap.remove(RELOAD_SENSOR_DATA);
                }
            } else if (characteristic.getUuid().equals(BluetoothLEHelper.GattInfo.BASE_WORK_MODE_UUID)) {
                WriteCallback writeCallbackByte = writeCallbackHashMap.get(RESET_TO_FACTORY);
                // 恢复出厂设置
                if (status == BluetoothGatt.GATT_SUCCESS) {
                    disconnect();
                    callback.onResetToFactorySettings(beacon, SUCCESS);
                    writeCallbackByte.onSuccess();
                    writeCallbackHashMap.remove(RESET_TO_FACTORY);
                } else {
                    callback.onResetToFactorySettings(beacon, FAILURE);
                    writeCallbackByte.onFailure(FAILURE);
                    writeCallbackHashMap.remove(RESET_TO_FACTORY);
                }
            } else if (characteristic.getUuid().equals(BluetoothLEHelper.GattInfo.BASE_SECURE_BROADCAST_UUID)) {
                WriteCallback writeCallbackByte = writeCallbackHashMap.get(WRITE_SECURE_BROADCAST_INTERVAL);
                // 动态变化 mm
                if (status == BluetoothGatt.GATT_SUCCESS) {
                    beacon.secureBroadcastInterval = secureBroadcastInterval;
                    callback.onWriteSecureBroadcastInterval(beacon, SUCCESS);
                    writeCallbackByte.onSuccess();
                    writeCallbackHashMap.remove(WRITE_SECURE_BROADCAST_INTERVAL);
                } else {
                    callback.onWriteSecureBroadcastInterval(beacon, FAILURE);
                    writeCallbackByte.onFailure(FAILURE);
                    writeCallbackHashMap.remove(WRITE_SECURE_BROADCAST_INTERVAL);
                }
            } else if (characteristic.getUuid().equals(BluetoothLEHelper.GattInfo.BASE_ENABLE_IBEACON_UUID)) {
                WriteCallback writeCallbackByteEnable = writeCallbackHashMap.get(ENABLE_IBEACON);
                WriteCallback writeCallbackByteDisable = writeCallbackHashMap.get(DISABLE_IBEACON);
                // 开启 iBeacon 功能
                if (status == BluetoothGatt.GATT_SUCCESS) {
                    beacon.isIBeaconEnabled = isIBeaconEnabled;
                    if (isIBeaconEnabled) {
                        callback.onEnableIBeacon(beacon, SUCCESS);
                        writeCallbackByteEnable.onSuccess();
                        writeCallbackHashMap.remove(ENABLE_IBEACON);
                    } else {
                        callback.onDisableIBeacon(beacon, SUCCESS);
                        writeCallbackByteDisable.onSuccess();
                        writeCallbackHashMap.remove(DISABLE_IBEACON);
                    }
                } else {
                    if (isIBeaconEnabled) {
                        callback.onEnableIBeacon(beacon, FAILURE);
                        writeCallbackByteEnable.onFailure(FAILURE);
                        writeCallbackHashMap.remove(ENABLE_IBEACON);
                    } else {
                        callback.onDisableIBeacon(beacon, FAILURE);
                        writeCallbackByteDisable.onFailure(FAILURE);
                        writeCallbackHashMap.remove(DISABLE_IBEACON);
                    }
                }
            } else if (characteristic.getUuid().equals(BluetoothLEHelper.GattInfo.BASE_BROADCAST_KEY_UUID)) {
                WriteCallback writeCallbackByte = writeCallbackHashMap.get(WRITE_BROADCAST_KEY);
                // 云子广播 key
                if (status == BluetoothGatt.GATT_SUCCESS) {
                    if (isKeyZero) {
                        beacon.isSecretEnabled = false;
                        isKeyZero = false;
                    } else {
                        beacon.isSecretEnabled = true;
                    }
                    callback.onWriteBroadcastKey(beacon, SUCCESS);
                    writeCallbackByte.onSuccess();
                    writeCallbackHashMap.remove(WRITE_BROADCAST_KEY);
                } else {
                    callback.onWriteBroadcastKey(beacon, FAILURE);
                    writeCallbackByte.onFailure(FAILURE);
                    writeCallbackHashMap.remove(WRITE_BROADCAST_KEY);
                }
            } else if (characteristic.getUuid().equals(BluetoothLEHelper.GattInfo.CMD_WRITE_UUID)) {
                // 写入 LED 成功
                if (status == BluetoothGatt.GATT_SUCCESS) {
                    callback.onFlashLightWitCommand(beacon, SUCCESS);
                } else {
                    callback.onFlashLightWitCommand(beacon, FAILURE);
                }
            }
            super.onCharacteristicWrite(gatt, characteristic, status);
        }

        /**
         * BLE 读取特征值回调
         *
         * @param gatt
         * @param characteristic
         * @param status
         */
        @Override
        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            if (SensoroBeaconManager.DEBUG) {
                Log.d(TAG, "onCharacteristicRead---status =" + status);
            }
            if (characteristic.getUuid().equals(BluetoothLEHelper.GattInfo.BASE_PARAMS_SETTINGS_UUID)) {
                if (SensoroBeaconManager.DEBUG) {
                    Log.d(TAG, "onCharacteristicRead---char is baseSetting");
                }
                // 读取 BaseSettings
                if (status == BluetoothGatt.GATT_SUCCESS) {
                    byte[] baseSetting = characteristic.getValue();
                    // 广播功率
                    BaseSettings.TransmitPower transmitPower = SensoroUtils.getTransmitPower((int) baseSetting[0] & 0xff);
                    baseSettings.setTransmitPower(transmitPower);
                    beacon.transmitPower = transmitPower;
                    // 广播间隔
                    BaseSettings.AdvertisingInterval advertisingInterval = SensoroUtils.getAdvertisingInterval((int) baseSetting[1] & 0xff);
                    baseSettings.setAdvertisingInterval(advertisingInterval);
                    beacon.advertisingInterval = advertisingInterval;
                    baseSettings.setEnergySavingMode(SensoroUtils.getEnergySavingMode((int) baseSetting[2] & 0xff));
                    byte measuredPower = (byte) ((byte) baseSetting[3] & 0xff);
                    baseSettings.setMeasuredPower(((int) measuredPower > 127) ? ((int) measuredPower - 256) : (int) measuredPower); // 保证 MeasuredPower 为 128~127
                    beacon.isPasswordEnabled = (baseSetting[4] != 0);
                    // baseSettings 赋值给 beacon
                    beacon.baseSettings = baseSettings;

                    if (isA0Beacon()) { // A0 型号 beacon 读取到 baseSetting 即连接成功
                        callback.onConnectedState(beacon, Beacon.CONNECTED, SUCCESS);
                        return;
                    } else if (isB0Beacon()) { // B0 型号 beacon 读取 baseSetting 成功。如果固件版本 2.3 以下，直接读取 sensoSettings，如果 2.3 以上，读取动态变化 mm 间隔
                        if (isB0Above2_3()) {
                            bluetoothLEHelper.getSecureBroadcastRotation();
                        } else {
                            bluetoothLEHelper.getSensoroSettings();
                        }
                    } else if (isC0Beacon()) { // C0 型号 beacon 读取 baseSetting 成功。
                        bluetoothLEHelper.getSecureBroadcastRotation();
                    } else {
                        callback.onConnectedState(beacon, Beacon.CONNECTED, ERROR_BEACON_MODEL);
                    }
                } else {
                    // 读取 BaseSettings 失败,回调链接失败,断开连接
                    callback.onConnectedState(beacon, Beacon.CONNECTED, FAILURE);
                    gatt.close();
                    if (SensoroBeaconManager.DEBUG) {
                        Log.d(TAG, "onCharacteristicRead---callback connect failure:get baseSetting failure");
                    }
                }
            } else if (characteristic.getUuid().equals(BluetoothLEHelper.GattInfo.BASE_SECURE_BROADCAST_UUID)) {
                if (SensoroBeaconManager.DEBUG) {
                    Log.d(TAG, "onCharacteristicRead---char is dynamic mm");
                }
                // 读取动态 mm 时间间隔
                if (status == BluetoothGatt.GATT_SUCCESS) {
                    byte[] secureBroadcastIntervalBytes = characteristic.getValue();
                    int secureBroadcastIntervalInt = (secureBroadcastIntervalBytes[0] & 0xff) + ((secureBroadcastIntervalBytes[1] & 0xff) << 8) + ((secureBroadcastIntervalBytes[2] & 0xff) << 16)
                            + ((secureBroadcastIntervalBytes[3] & 0xff) << 24);
                    beacon.secureBroadcastInterval = SensoroUtils.getSecureBroadcastInterval(secureBroadcastIntervalInt);

                    // 读取成功,如果 B0-3.0,C0-3.0 以上,读取工作模式
                    if ((beacon.hardwareModelName.equals(Beacon.HV_B0) && beacon.firmwareVersion.equals(Beacon.FV_30))
                            || (beacon.hardwareModelName.equals(Beacon.HV_C0) && beacon.firmwareVersion.equals(Beacon.FV_30))) {
                        bluetoothLEHelper.getWorkMode();
                    } else {
                        bluetoothLEHelper.getSensoroSettings();
                    }
                } else {
                    // 读取动态mm时间间隔失败,回调链接失败,断开连接
                    callback.onConnectedState(beacon, Beacon.CONNECTED, FAILURE);
                    gatt.close();
                    if (SensoroBeaconManager.DEBUG) {
                        Log.d(TAG, "onCharacteristicRead---callback connect failure:get dynamic mm failure");
                    }
                }
            } else if (characteristic.getUuid().equals(BluetoothLEHelper.GattInfo.BASE_ENABLE_IBEACON_UUID)) {
                if (SensoroBeaconManager.DEBUG) {
                    Log.d(TAG, "onCharacteristicRead---char is work mode");
                }
                // 读取云子是否开启 iBeacon 功能
                if (status == BluetoothGatt.GATT_SUCCESS) {
                    byte[] enableIBeaconBytes = characteristic.getValue();
                    beacon.isIBeaconEnabled = enableIBeaconBytes[0] != 0;
                    bluetoothLEHelper.getSensoroSettings();
                } else {
                    // 读取工作模式,回调链接失败,断开连接
                    callback.onConnectedState(beacon, Beacon.CONNECTED, FAILURE);
                    gatt.close();
                    if (SensoroBeaconManager.DEBUG) {
                        Log.d(TAG, "onCharacteristicRead---callback connect failure:get work mode failure");
                    }
                }
            } else if (characteristic.getUuid().equals(BluetoothLEHelper.GattInfo.SENSO_PARAMS_SETTINGS_UUID)) {
                if (SensoroBeaconManager.DEBUG) {
                    Log.d(TAG, "onCharacteristicRead---char is sensoSetting");
                }
                // 读取SensoSettings
                if (status == BluetoothGatt.GATT_SUCCESS) {
                    byte[] sensoSetting = characteristic.getValue();
                    sensorSettings.setTemperatureSamplingInterval((sensoSetting[0] & 0xff) + ((sensoSetting[1] & 0xff) << 8));
                    sensorSettings.setLightSamplingInterval((sensoSetting[2] & 0xff) + ((sensoSetting[3] & 0xff) << 8));
                    sensorSettings.setAccelerometerSensitivity(SensoroUtils.getAccleromerterSensitivity((sensoSetting[4] & 0xff) + ((sensoSetting[5] & 0xff) << 8)));
                    // sensorSettings 赋值给beacon
                    beacon.sensorSettings = sensorSettings;
                    // 读取 SensoSettings 成功,监听温度、光强和加速度传感器特征值
                    bluetoothLEHelper.listenTemperatureChar();
                } else {
                    // 读取读取SensoSettings失败,回调链接失败,断开连接
                    callback.onConnectedState(beacon, Beacon.CONNECTED, FAILURE);
                    gatt.close();
                    if (SensoroBeaconManager.DEBUG) {
                        Log.d(TAG, "onCharacteristicRead---callback connect failure:get sensoSetting failure");
                    }
                }
            }
            super.onCharacteristicRead(gatt, characteristic, status);
        }

        /**
         * BLE 特征值更新
         *
         * @param gatt
         * @param characteristic
         */
        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
            if (characteristic.getUuid().equals(BluetoothLEHelper.GattInfo.SENSO_TEMPERATURE_UUID)) {
                byte[] temperatureByte = characteristic.getValue();
                int temperature = (int) (temperatureByte[0] & 0xff);
                if (temperature == 0xff) {
                    beacon.temperature = null;
                } else {
                    beacon.temperature = temperature;
                }
                callback.onUpdateTemperatureData(beacon, beacon.temperature);
            } else if (characteristic.getUuid().equals(BluetoothLEHelper.GattInfo.SENSO_BRIGHT_UUID)) {
                byte[] brightByte = characteristic.getValue();

                // 计算光线强度值
                int luxRawLow = (int) (brightByte[0] & 0xff);
                int luxRawHigh = (int) (brightByte[1] & 0xff);
                if (luxRawHigh == 0xff) {
                    beacon.light = null;
                } else {
                    beacon.light = calculateLux(luxRawHigh, luxRawLow);
                }
                callback.onUpdateLightData(beacon, beacon.light);
            } else if (characteristic.getUuid().equals(BluetoothLEHelper.GattInfo.SENSO_IS_MOVING_UUID)) {
                byte[] stateByte = characteristic.getValue();
                int state = stateByte[0] & 0xff;
                Beacon.MovingState movingState = Beacon.MovingState.getMovingState(state);
                beacon.movingState = movingState;
                callback.onUpdateMovingState(beacon, beacon.movingState);
            } else if (characteristic.getUuid().equals(BluetoothLEHelper.GattInfo.SENSO_ACCELERATOR_UUID)) {
                byte[] countBytes = characteristic.getValue();
                int count = (countBytes[0] & 0xff) + ((countBytes[1] & 0xff) << 8) + ((countBytes[2] & 0xff) << 16);
                beacon.accelerometerCount = count;
                callback.onUpdateAccelerometerCount(beacon, count);
            }
            super.onCharacteristicChanged(gatt, characteristic);
        }

        @Override
        public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
            if (descriptor.getUuid().equals(BluetoothLEHelper.GattInfo.CLIENT_CHARACTERISTIC_CONFIG)) {
                if (status == BluetoothGatt.GATT_SUCCESS) {
                    if (listenType == 0) {
                        // 监听温度成功,监听光强
                        listenType = 1;
                        bluetoothLEHelper.listenBrightnessChar();
                    } else if (listenType == 1) {
                        listenType = 2;
                        // 监听光强成功,监听加速度传感器次数
                        bluetoothLEHelper.listenAcceleratorCountChar();
                    } else if (listenType == 2) {
                        listenType = 3;
                        // 监听加速度传感器次数成功监听加速度传感器是否移动
                        bluetoothLEHelper.listenAcceleratorMovingChar();
                    } else if (listenType == 3) {
                        // 监听加速度传感器是否移动回调连接成功
                        callback.onConnectedState(beacon, Beacon.CONNECTED, SUCCESS);
                        if (SensoroBeaconManager.DEBUG) {
                            Log.d(TAG, "onDescriptorWrite---callback connect success");
                        }
                        isConnected = true;
                    }
                }
            }
            super.onDescriptorWrite(gatt, descriptor, status);
        }

    };

    private double calculateLux(int luxRawHigh, int luxRawLow) {
        return Math.pow(2, luxRawHigh / 16) * ((luxRawHigh % 16) * 16 + luxRawLow % 16) * 0.045;
    }

    /**
     * The callback interface of the beacon.
     */
    public abstract interface BeaconConnectionCallback {

        /**
         * The callback method when the state of the connection is changed.
         *
         * @param beacon   The current beacon.
         * @param newState The new connection state of connecting the beacon. It can
         *                 be one of CONNECTED or DISCONNECTED. See
         *                 {@link Beacon#CONNECTED} and {@link Beacon#DISCONNECTED}
         * @param status   The result of connecting the beacon.It can be SUCCESS or
         *                 FAILURE. See
         *                 {@link com.sensoro.beacon.kit.SensoroBeaconConnection#SUCCESS}
         *                 and
         *                 {@link com.sensoro.beacon.kit.SensoroBeaconConnection#FAILURE}
         */
        public void onConnectedState(Beacon beacon, int newState, int status);

        /**
         * The callback method of setting a new password.
         *
         * @param beacon The current beacon.
         * @param status the result of setting new password.It can be SUCCESS or
         *               FAILURE. See
         *               {@link com.sensoro.beacon.kit.SensoroBeaconConnection#SUCCESS}
         *               and
         *               {@link com.sensoro.beacon.kit.SensoroBeaconConnection#FAILURE}
         */
        public void onWritePassword(Beacon beacon, int status);

        /**
         * The callback method of disabling password.
         *
         * @param beacon The current beacon.
         * @param status The result of disabling the password.It can be SUCCESS or
         *               FAILURE. See
         *               {@link com.sensoro.beacon.kit.SensoroBeaconConnection#SUCCESS}
         *               and
         *               {@link com.sensoro.beacon.kit.SensoroBeaconConnection#FAILURE}
         */
        public void onDisablePassword(Beacon beacon, int status);

        /**
         * The callback method of checking password.
         *
         * @param beacon The current beacon.
         * @param status The result of checking the password.It can be SUCCESS or
         *               FAILURE. See
         *               {@link com.sensoro.beacon.kit.SensoroBeaconConnection#SUCCESS}
         *               and
         *               {@link com.sensoro.beacon.kit.SensoroBeaconConnection#FAILURE}
         */
        public void onRequireWritePermission(Beacon beacon, int status);

        /**
         * The callback method of modifying the base settings.
         *
         * @param beacon The current beacon.
         * @param status The result of setting base settings.It can be SUCCESS or
         *               FAILURE. See
         *               {@link com.sensoro.beacon.kit.SensoroBeaconConnection#SUCCESS}
         *               and
         *               {@link com.sensoro.beacon.kit.SensoroBeaconConnection#FAILURE}
         */
        public void onWriteBaseSetting(Beacon beacon, int status);

        /**
         * The callback method of modifying the sensor setting.
         *
         * @param beacon The current beacon.
         * @param status The result of setting the sensor settings.It can be one of
         *               SUCCESS or FAILURE. See
         *               {@link com.sensoro.beacon.kit.SensoroBeaconConnection#SUCCESS}
         *               and
         *               {@link com.sensoro.beacon.kit.SensoroBeaconConnection#FAILURE}
         */
        public void onWirteSensorSetting(Beacon beacon, int status);

        /**
         * The callback method of modifying the major and minor.
         *
         * @param beacon The current beacon.
         * @param status The result of setting the major and minor.It can be one of
         *               SUCCESS or FAILURE. See
         *               {@link com.sensoro.beacon.kit.SensoroBeaconConnection#SUCCESS}
         *               and
         *               {@link com.sensoro.beacon.kit.SensoroBeaconConnection#FAILURE}
         */
        public void onWriteMajorMinor(Beacon beacon, int status);

        /**
         * The callback method of modifying the proximity UUID.
         *
         * @param beacon The current beacon.
         * @param status The result of setting the UUID.It can be one of SUCCESS or
         *               FAILURE. See
         *               {@link com.sensoro.beacon.kit.SensoroBeaconConnection#SUCCESS}
         *               and
         *               {@link com.sensoro.beacon.kit.SensoroBeaconConnection#FAILURE}
         */
        public void onWriteProximityUUID(Beacon beacon, int status);

        /**
         * The callback method of modifying the interval of secure broadcast of beacon.
         *
         * @param beacon The current beacon.
         * @param status The result of setting the rotation of secure broadcast of
         *               beacon.It can be one of SUCCESS or FAILURE. See
         *               {@link com.sensoro.beacon.kit.SensoroBeaconConnection#SUCCESS}
         *               and
         *               {@link com.sensoro.beacon.kit.SensoroBeaconConnection#FAILURE}
         */
        public void onWriteSecureBroadcastInterval(Beacon beacon, int status);

        /**
         * The callback method of enable iBeacon function.
         *
         * @param beacon The current beacon.
         * @param status The result of setting the rotation of secure broadcast of
         *               beacon.It can be one of SUCCESS or FAILURE. See
         *               {@link com.sensoro.beacon.kit.SensoroBeaconConnection#SUCCESS}
         *               and
         *               {@link com.sensoro.beacon.kit.SensoroBeaconConnection#FAILURE}
         */
        public void onEnableIBeacon(Beacon beacon, int status);

        /**
         * The callback method of disable iBeacon function.
         *
         * @param beacon The current beacon.
         * @param status The result of setting the rotation of secure broadcast of
         *               beacon.It can be one of SUCCESS or FAILURE. See
         *               {@link com.sensoro.beacon.kit.SensoroBeaconConnection#SUCCESS}
         *               and
         *               {@link com.sensoro.beacon.kit.SensoroBeaconConnection#FAILURE}
         */
        public void onDisableIBeacon(Beacon beacon, int status);

        /**
         * The callback method of write broadcast key of Yunzi.
         *
         * @param beacon The current beacon.
         * @param status The result of setting the rotation of secure broadcast of
         *               beacon.It can be one of SUCCESS or FAILURE. See
         *               {@link com.sensoro.beacon.kit.SensoroBeaconConnection#SUCCESS}
         *               and
         *               {@link com.sensoro.beacon.kit.SensoroBeaconConnection#FAILURE}
         */
        public void onWriteBroadcastKey(Beacon beacon, int status);

        /**
         * The callback method of resetting to factory.
         *
         * @param beacon The current beacon.
         * @param status Return the result of resetting to factory.It can be one of
         *               SUCCESS or FAILURE. See
         *               {@link com.sensoro.beacon.kit.SensoroBeaconConnection#SUCCESS}
         *               and
         *               {@link com.sensoro.beacon.kit.SensoroBeaconConnection#FAILURE}
         */
        public void onResetToFactorySettings(Beacon beacon, int status);

        /**
         * The callback method of resetting acceleration sensor count.
         *
         * @param beacon The current beacon.
         * @param status The result of resetting the accelerator count.It can be
         *               one of SUCCESS or FAILURE. See
         *               {@link com.sensoro.beacon.kit.SensoroBeaconConnection#SUCCESS}
         *               and
         *               {@link com.sensoro.beacon.kit.SensoroBeaconConnection#FAILURE}
         */
        public void onResetAcceleratorCount(Beacon beacon, int status);

        /**
         * The callback method of updating sensor data.The method will be called
         * when calling
         * {@link com.sensoro.beacon.kit.SensoroBeaconConnection#reloadSensorData}
         *
         * @param beacon The current beacon.
         * @param status The result of updating the senso data.It can be one of
         *               SUCCESS or FAILURE. See
         *               {@link com.sensoro.beacon.kit.SensoroBeaconConnection#SUCCESS}
         *               and
         *               {@link com.sensoro.beacon.kit.SensoroBeaconConnection#FAILURE}
         */
        public void onReloadSensorData(Beacon beacon, int status);

        /**
         * This method is called automatically once the temperature condition is
         * changing after connecting the beacon successfully.
         *
         * @param beacon      The current beacon.
         * @param temperature temperature of environment condition
         */
        public void onUpdateTemperatureData(Beacon beacon, Integer temperature);

        /**
         * This method is called automatically once the light condition is
         * changing after connecting the beacon successfully.
         *
         * @param beacon        The current beacon.
         * @param brightnessLux The illumination intensity.
         */
        public void onUpdateLightData(Beacon beacon, Double brightnessLux);

        /**
         * This method is called automatically once the acceleration sensor
         * state is changing after connecting the beacon successfully.
         *
         * @param beacon      The current beacon.
         * @param movingState Current moving state of the beacon.
         */
        public void onUpdateMovingState(Beacon beacon, Beacon.MovingState movingState);

        /**
         * This method is called automatically once the acceleration sensor
         * state is changing after connecting the beacon successfully.
         *
         * @param beacon The current beacon.
         * @param count  Moving count of the beacon.
         */
        public void onUpdateAccelerometerCount(Beacon beacon, int count);

        /**
         * The callback method of writing LED.
         *
         * @param beacon The current beacon.
         * @param status The result of setting the UUID.It can be one of SUCCESS or
         *               FAILURE. See
         *               {@link com.sensoro.beacon.kit.SensoroBeaconConnection#SUCCESS}
         *               and
         *               {@link com.sensoro.beacon.kit.SensoroBeaconConnection#FAILURE}
         */
        public void onFlashLightWitCommand(Beacon beacon, int status);
    }

    /**
     * The exception of the SDK.
     */
    public class SensoroException extends Exception {
        private static final long serialVersionUID = 5433959851204242014L;

        public SensoroException() {
        }

        public SensoroException(String message) {
            super(message);
        }
    }
}
